Improve V3List user interface (#4996)
This commit is contained in:
parent
4df9e2e0e5
commit
98206a4f04
|
|
@ -97,8 +97,8 @@ protected:
|
|||
result = vertexp->user();
|
||||
break;
|
||||
case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (latchCheckInternal(castVertexp(edgep->top()))) {
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (latchCheckInternal(castVertexp(edge.top()))) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -106,9 +106,8 @@ protected:
|
|||
break;
|
||||
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
|
||||
// A BRANCH vertex always has exactly 2 siblings
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
|
||||
LatchDetectGraphVertex* const elsp
|
||||
= castVertexp(vertexp->outBeginp()->outNextp()->top());
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outEdges().frontp()->top());
|
||||
LatchDetectGraphVertex* const elsp = castVertexp(vertexp->outEdges().backp()->top());
|
||||
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
|
||||
break;
|
||||
}
|
||||
|
|
@ -170,7 +169,7 @@ public:
|
|||
for (const auto& vrp : m_outputs) {
|
||||
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
|
||||
vertp->user(true); // Identify the output vertex we are checking paths _to_
|
||||
if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true;
|
||||
if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true;
|
||||
if (latch_detected && !latch_expected) {
|
||||
nodep->v3warn(
|
||||
LATCH,
|
||||
|
|
|
|||
|
|
@ -38,20 +38,21 @@ void DfgGraph::addGraph(DfgGraph& other) {
|
|||
m_size += other.m_size;
|
||||
other.m_size = 0;
|
||||
|
||||
const auto moveVertexList = [this](V3List<DfgVertex*>& src, V3List<DfgVertex*>& dst) {
|
||||
if (DfgVertex* vtxp = src.begin()) {
|
||||
vtxp->m_verticesEnt.moveAppend(src, dst, vtxp);
|
||||
do {
|
||||
vtxp->m_userCnt = 0;
|
||||
vtxp->m_graphp = this;
|
||||
vtxp = vtxp->verticesNext();
|
||||
} while (vtxp);
|
||||
}
|
||||
};
|
||||
|
||||
moveVertexList(other.m_varVertices, m_varVertices);
|
||||
moveVertexList(other.m_constVertices, m_constVertices);
|
||||
moveVertexList(other.m_opVertices, m_opVertices);
|
||||
for (DfgVertexVar& vtx : other.m_varVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_varVertices.splice(m_varVertices.end(), other.m_varVertices);
|
||||
for (DfgConst& vtx : other.m_constVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_constVertices.splice(m_constVertices.end(), other.m_constVertices);
|
||||
for (DfgVertex& vtx : other.m_opVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_opVertices.splice(m_opVertices.end(), other.m_opVertices);
|
||||
}
|
||||
|
||||
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
||||
|
|
|
|||
670
src/V3Dfg.h
670
src/V3Dfg.h
|
|
@ -55,6 +55,8 @@
|
|||
#endif
|
||||
|
||||
class DfgEdge;
|
||||
class DfgVertex;
|
||||
class DfgGraph;
|
||||
class DfgVisitor;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -89,140 +91,6 @@ constexpr bool operator==(VDfgType lhs, VDfgType::en rhs) { return lhs.m_e == rh
|
|||
constexpr bool operator==(VDfgType::en lhs, VDfgType rhs) { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VDfgType& t) { return os << t.ascii(); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
||||
// Variables and constants make up a significant proportion of vertices (40-50% was observed
|
||||
// in large designs), and they can often be treated specially in algorithms, which in turn
|
||||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
V3List<DfgVertex*> m_varVertices; // The variable vertices in the graph
|
||||
V3List<DfgVertex*> m_constVertices; // The constant vertices in the graph
|
||||
V3List<DfgVertex*> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
inline void addVertex(DfgVertex& vtx);
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
AstModule* modulep() const { return m_modulep; }
|
||||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
|
||||
// Access to vertex lists for faster iteration in important contexts
|
||||
inline DfgVertexVar* varVerticesBeginp() const;
|
||||
inline DfgVertexVar* varVerticesRbeginp() const;
|
||||
inline DfgConst* constVerticesBeginp() const;
|
||||
inline DfgConst* constVerticesRbeginp() const;
|
||||
inline DfgVertex* opVerticesBeginp() const;
|
||||
inline DfgVertex* opVerticesRbeginp() const;
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
|
||||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
inline void forEachVertex(std::function<void(DfgVertex&)> f);
|
||||
|
||||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
|
||||
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
|
||||
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
|
||||
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
|
||||
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
|
||||
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
|
||||
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
|
||||
// it was originally connected.
|
||||
std::vector<std::unique_ptr<DfgGraph>>
|
||||
extractCyclicComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
|
||||
// the graph which is included in the output.
|
||||
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to
|
||||
// the name of the graph which is included in the output.
|
||||
void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
|
||||
// added to the name of the graph, which is included in the file name and the output.
|
||||
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump upstream (source) logic cone starting from given vertex into a file with the given
|
||||
// 'fileName'. 'name' is the name of the graph, which is included in the output.
|
||||
void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
|
||||
const string& name = "") const VL_MT_DISABLED;
|
||||
// Dump all individual logic cones driving external variables in Graphviz format into separate
|
||||
// new automatically numbered debug files. 'label' is added to the name of the graph, which is
|
||||
// included in the file names and the output. This is useful for very large graphs that are
|
||||
// otherwise difficult to browse visually due to their size.
|
||||
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph edge
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -264,7 +132,8 @@ class DfgVertex VL_NOT_FINAL {
|
|||
using UserDataStorage = void*; // Storage allocated for user data
|
||||
|
||||
// STATE
|
||||
V3ListEnt<DfgVertex*> m_verticesEnt; // V3List handle of this vertex, kept under the DfgGraph
|
||||
V3ListLinks<DfgVertex> m_links; // V3List links
|
||||
|
||||
protected:
|
||||
DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex
|
||||
FileLine* const m_filelinep; // Source location
|
||||
|
|
@ -280,6 +149,14 @@ protected:
|
|||
public:
|
||||
virtual ~DfgVertex() VL_MT_DISABLED;
|
||||
|
||||
private:
|
||||
V3ListLinks<DfgVertex>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List type that can store Vertex (which must be a DfgVertex) instances via m_links
|
||||
template <typename Vertex>
|
||||
using List = V3List<DfgVertex, &DfgVertex::links, Vertex>;
|
||||
|
||||
// METHODS
|
||||
private:
|
||||
// Visitor accept method
|
||||
|
|
@ -349,54 +226,15 @@ public:
|
|||
|
||||
// Retrieve user data, constructing it fresh on first try.
|
||||
template <typename T>
|
||||
T& user() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
// cppcheck-has-bug-suppress uninitvar
|
||||
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
|
||||
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
inline T& user();
|
||||
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T>
|
||||
T& getUser() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
inline T& getUser();
|
||||
|
||||
// Set user data, becomes current.
|
||||
template <typename T>
|
||||
typename std::enable_if<sizeof(T) <= sizeof(void*), void>::type setUser(T value) {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
}
|
||||
inline void setUser(T value);
|
||||
|
||||
// Width of result
|
||||
uint32_t width() const {
|
||||
|
|
@ -449,10 +287,6 @@ public:
|
|||
// Relink all sinks to be driven from the given new source
|
||||
void replaceWith(DfgVertex* newSourcep) VL_MT_DISABLED;
|
||||
|
||||
// Access to vertex list for faster iteration in important contexts
|
||||
DfgVertex* verticesNext() const { return m_verticesEnt.nextp(); }
|
||||
DfgVertex* verticesPrev() const { return m_verticesEnt.prevp(); }
|
||||
|
||||
// Calls given function 'f' for each source vertex of this vertex
|
||||
// Unconnected source edges are not iterated.
|
||||
inline void forEachSource(std::function<void(DfgVertex&)> f) VL_MT_DISABLED;
|
||||
|
|
@ -567,9 +401,6 @@ public:
|
|||
virtual const string srcName(size_t idx) const = 0;
|
||||
};
|
||||
|
||||
// Specializations of privateTypeTest
|
||||
#include "V3Dfg__gen_type_tests.h" // From ./astgen
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dfg vertex visitor
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -583,152 +414,6 @@ public:
|
|||
#include "V3Dfg__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgGraph::addVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
++m_size;
|
||||
if (vtx.is<DfgConst>()) {
|
||||
vtx.m_verticesEnt.pushBack(m_constVertices, &vtx);
|
||||
} else if (vtx.is<DfgVertexVar>()) {
|
||||
vtx.m_verticesEnt.pushBack(m_varVertices, &vtx);
|
||||
} else {
|
||||
vtx.m_verticesEnt.pushBack(m_opVertices, &vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
|
||||
void DfgGraph::removeVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
--m_size;
|
||||
if (vtx.is<DfgConst>()) {
|
||||
vtx.m_verticesEnt.unlink(m_constVertices, &vtx);
|
||||
} else if (vtx.is<DfgVertexVar>()) {
|
||||
vtx.m_verticesEnt.unlink(m_varVertices, &vtx);
|
||||
} else {
|
||||
vtx.m_verticesEnt.unlink(m_opVertices, &vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertex *vtxp = m_varVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
for (DfgVertex *vtxp = m_constVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
for (DfgVertex *vtxp = m_opVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertex* vtxp = m_varVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
for (const DfgVertex* vtxp = m_constVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
for (const DfgVertex* vtxp = m_opVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
|
||||
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep->m_sinkp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
const DfgEdge& edge = edgesp[i];
|
||||
if (p(edge, i)) return &edge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
|
||||
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be subclass of 'DfgVertex'");
|
||||
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
|
||||
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
|
||||
if (p(*sinkp)) return sinkp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink() const {
|
||||
static_assert(!std::is_same<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be proper subclass of 'DfgVertex'");
|
||||
return findSink<Vertex>([](const Vertex&) { return true; });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertex sub-types follow
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -903,20 +588,281 @@ public:
|
|||
// The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeExpr nodes
|
||||
#include "V3Dfg__gen_auto_classes.h"
|
||||
|
||||
DfgVertexVar* DfgGraph::varVerticesBeginp() const {
|
||||
return static_cast<DfgVertexVar*>(m_varVertices.begin());
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
||||
// Variables and constants make up a significant proportion of vertices (40-50% was observed
|
||||
// in large designs), and they can often be treated specially in algorithms, which in turn
|
||||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
|
||||
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
|
||||
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
inline void addVertex(DfgVertex& vtx);
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
AstModule* modulep() const { return m_modulep; }
|
||||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
|
||||
// Access to vertex lists
|
||||
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
|
||||
const DfgVertex::List<DfgVertexVar>& varVertices() const { return m_varVertices; }
|
||||
DfgVertex::List<DfgConst>& constVertices() { return m_constVertices; }
|
||||
const DfgVertex::List<DfgConst>& constVertices() const { return m_constVertices; }
|
||||
DfgVertex::List<DfgVertex>& opVertices() { return m_opVertices; }
|
||||
const DfgVertex::List<DfgVertex>& opVertices() const { return m_opVertices; }
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
|
||||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
inline void forEachVertex(std::function<void(DfgVertex&)> f);
|
||||
|
||||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
|
||||
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
|
||||
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
|
||||
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
|
||||
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
|
||||
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
|
||||
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
|
||||
// it was originally connected.
|
||||
std::vector<std::unique_ptr<DfgGraph>>
|
||||
extractCyclicComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
|
||||
// the graph which is included in the output.
|
||||
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to
|
||||
// the name of the graph which is included in the output.
|
||||
void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
|
||||
// added to the name of the graph, which is included in the file name and the output.
|
||||
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump upstream (source) logic cone starting from given vertex into a file with the given
|
||||
// 'fileName'. 'name' is the name of the graph, which is included in the output.
|
||||
void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
|
||||
const string& name = "") const VL_MT_DISABLED;
|
||||
// Dump all individual logic cones driving external variables in Graphviz format into separate
|
||||
// new automatically numbered debug files. 'label' is added to the name of the graph, which is
|
||||
// included in the file names and the output. This is useful for very large graphs that are
|
||||
// otherwise difficult to browse visually due to their size.
|
||||
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
// Specializations of privateTypeTest
|
||||
#include "V3Dfg__gen_type_tests.h" // From ./astgen
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgVertex
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::user() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
// cppcheck-has-bug-suppress uninitvar
|
||||
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
|
||||
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
DfgVertexVar* DfgGraph::varVerticesRbeginp() const {
|
||||
return static_cast<DfgVertexVar*>(m_varVertices.rbegin());
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::getUser() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
DfgConst* DfgGraph::constVerticesBeginp() const {
|
||||
return static_cast<DfgConst*>(m_constVertices.begin());
|
||||
|
||||
template <typename T>
|
||||
void DfgVertex::setUser(T value) {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
}
|
||||
DfgConst* DfgGraph::constVerticesRbeginp() const {
|
||||
return static_cast<DfgConst*>(m_constVertices.rbegin());
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
|
||||
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep->m_sinkp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
const DfgEdge& edge = edgesp[i];
|
||||
if (p(edge, i)) return &edge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
|
||||
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be subclass of 'DfgVertex'");
|
||||
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
|
||||
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
|
||||
if (p(*sinkp)) return sinkp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink() const {
|
||||
static_assert(!std::is_same<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be proper subclass of 'DfgVertex'");
|
||||
return findSink<Vertex>([](const Vertex&) { return true; });
|
||||
}
|
||||
DfgVertex* DfgGraph::opVerticesBeginp() const { return m_opVertices.begin(); }
|
||||
DfgVertex* DfgGraph::opVerticesRbeginp() const { return m_opVertices.rbegin(); }
|
||||
|
||||
bool DfgVertex::isZero() const {
|
||||
if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero();
|
||||
|
|
@ -928,4 +874,48 @@ bool DfgVertex::isOnes() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgGraph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgGraph::addVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
++m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.linkBack(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.linkBack(vVtxp);
|
||||
} else {
|
||||
m_opVertices.linkBack(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
|
||||
void DfgGraph::removeVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
--m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.unlink(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.unlink(vVtxp);
|
||||
} else {
|
||||
m_opVertices.unlink(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp);
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertexVar& vtx : m_varVertices) f(vtx);
|
||||
for (const DfgConst& vtx : m_constVertices) f(vtx);
|
||||
for (const DfgVertex& vtx : m_opVertices) f(vtx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -44,13 +44,12 @@ class SplitIntoComponents final {
|
|||
queue.reserve(m_dfg.size());
|
||||
|
||||
// any sort of interesting logic must involve a variable, so we only need to iterate them
|
||||
for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// If already assigned this vertex to a component, then continue
|
||||
if (vtxp->user<size_t>()) continue;
|
||||
if (vtx.user<size_t>()) continue;
|
||||
|
||||
// Start depth first traversal at this vertex
|
||||
queue.push_back(vtxp);
|
||||
queue.push_back(&vtx);
|
||||
|
||||
// Depth first traversal
|
||||
do {
|
||||
|
|
@ -74,16 +73,15 @@ class SplitIntoComponents final {
|
|||
}
|
||||
}
|
||||
|
||||
void moveVertices(DfgVertex* headp) {
|
||||
for (DfgVertex *vtxp = headp, *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
DfgVertex& vtx = *vtxp;
|
||||
if (const size_t component = vtx.user<size_t>()) {
|
||||
m_dfg.removeVertex(vtx);
|
||||
m_components[component - 1]->addVertex(vtx);
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
if (const size_t component = vtxp->user<size_t>()) {
|
||||
m_dfg.removeVertex(*vtxp);
|
||||
m_components[component - 1]->addVertex(*vtxp);
|
||||
} else {
|
||||
// This vertex is not connected to a variable and is hence unused, remove here
|
||||
vtx.unlinkDelete(m_dfg);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,9 +99,9 @@ class SplitIntoComponents final {
|
|||
m_components[i - 1].reset(new DfgGraph{*m_dfg.modulep(), m_prefix + cvtToStr(i - 1)});
|
||||
}
|
||||
// Move the vertices to the component graphs
|
||||
moveVertices(m_dfg.varVerticesBeginp());
|
||||
moveVertices(m_dfg.constVerticesBeginp());
|
||||
moveVertices(m_dfg.opVerticesBeginp());
|
||||
moveVertices(m_dfg.varVertices());
|
||||
moveVertices(m_dfg.constVertices());
|
||||
moveVertices(m_dfg.opVertices());
|
||||
//
|
||||
UASSERT(m_dfg.size() == 0, "'this' DfgGraph should have been emptied");
|
||||
}
|
||||
|
|
@ -238,9 +236,8 @@ class ExtractCyclicComponents final {
|
|||
// We can leverage some properties of the input graph to gain a bit of speed. Firstly, we
|
||||
// know constant nodes have no in edges, so they cannot be part of a non-trivial SCC. Mark
|
||||
// them as such without starting a whole traversal.
|
||||
for (DfgConst *vtxp = m_dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
VertexState& vtxState = allocState(*vtxp);
|
||||
for (DfgConst& vtx : m_dfg.constVertices()) {
|
||||
VertexState& vtxState = allocState(vtx);
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
}
|
||||
|
|
@ -248,16 +245,15 @@ class ExtractCyclicComponents final {
|
|||
// Next, we know that all SCCs must include a variable (as the input graph was converted
|
||||
// from an AST, we can only have a cycle by going through a variable), so we only start
|
||||
// traversals through them, and only if we know they have both in and out edges.
|
||||
for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (vtxp->arity() > 0 && vtxp->hasSinks()) {
|
||||
VertexState& vtxState = getOrAllocState(*vtxp);
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
if (vtx.arity() > 0 && vtx.hasSinks()) {
|
||||
VertexState& vtxState = getOrAllocState(vtx);
|
||||
// If not yet visited, start a traversal
|
||||
if (vtxState.index == UNASSIGNED) visitColorSCCs(*vtxp, vtxState);
|
||||
if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState);
|
||||
} else {
|
||||
VertexState& vtxState = getOrAllocState(*vtxp);
|
||||
VertexState& vtxState = getOrAllocState(vtx);
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
|
||||
vtxp, "Non circular variable must be in a trivial SCC"););
|
||||
&vtx, "Non circular variable must be in a trivial SCC"););
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
}
|
||||
|
|
@ -265,9 +261,8 @@ class ExtractCyclicComponents final {
|
|||
|
||||
// Finally, everything we did not visit through the traversal of a variable cannot be in an
|
||||
// SCC, (otherwise we would have found it from a variable).
|
||||
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
VertexState& vtxState = getOrAllocState(*vtxp);
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
VertexState& vtxState = getOrAllocState(vtx);
|
||||
if (vtxState.index == UNASSIGNED) {
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
|
|
@ -306,9 +301,7 @@ class ExtractCyclicComponents final {
|
|||
// Ensure that component boundaries are always at variables, by merging SCCs. Merging stops
|
||||
// at variable boundaries, so we don't need to iterate variables. Constants are reachable
|
||||
// from their sinks, or are unused, so we don't need to iterate them either.
|
||||
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
DfgVertex& vtx = *vtxp;
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Start DFS from each vertex that is in a non-trivial SCC, and merge everything
|
||||
// that is reachable from it into this component.
|
||||
if (const size_t target = state(vtx).component) visitMergeSCCs(vtx, target);
|
||||
|
|
@ -400,8 +393,7 @@ class ExtractCyclicComponents final {
|
|||
|
||||
static void packSources(DfgGraph& dfg) {
|
||||
// Remove undriven variable sources
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) {
|
||||
if (DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>()) {
|
||||
varp->packSources();
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
|
|
@ -419,9 +411,9 @@ class ExtractCyclicComponents final {
|
|||
}
|
||||
}
|
||||
|
||||
void moveVertices(DfgVertex* headp) {
|
||||
for (DfgVertex *vtxp = headp, *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
DfgVertex& vtx = *vtxp;
|
||||
if (const size_t component = state(vtx).component) {
|
||||
m_dfg.removeVertex(vtx);
|
||||
|
|
@ -481,17 +473,12 @@ class ExtractCyclicComponents final {
|
|||
// earlier merging of components ensured crossing in fact only happen at variable
|
||||
// boundaries). Note that fixing up the edges can create clones of variables. Clones do
|
||||
// not need fixing up, so we do not need to iterate them.
|
||||
DfgVertex* const lastp = m_dfg.varVerticesRbeginp();
|
||||
for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
// It is possible the last vertex (with a nullptr for 'nextp') gets cloned, and hence
|
||||
// it's 'nextp' would become none nullptr as the clone is added. However, we don't need
|
||||
// to iterate clones anyway, so it's ok to get the 'nextp' early in the loop.
|
||||
nextp = vtxp->verticesNext();
|
||||
DfgVertexVar& vtx = *vtxp;
|
||||
DfgVertex* const lastp = m_dfg.varVertices().backp();
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// Fix up the edges crossing components
|
||||
fixEdges(vtx);
|
||||
// Don't iterate clones added during this loop
|
||||
if (vtxp == lastp) break;
|
||||
if (&vtx == lastp) break;
|
||||
}
|
||||
|
||||
// Pack sources of variables to remove the now undriven inputs
|
||||
|
|
@ -507,9 +494,9 @@ class ExtractCyclicComponents final {
|
|||
|
||||
// Move other vertices to their component graphs
|
||||
// After this, vertex states are invalid as we moved the vertices
|
||||
moveVertices(m_dfg.varVerticesBeginp());
|
||||
moveVertices(m_dfg.constVerticesBeginp());
|
||||
moveVertices(m_dfg.opVerticesBeginp());
|
||||
moveVertices(m_dfg.varVertices());
|
||||
moveVertices(m_dfg.constVertices());
|
||||
moveVertices(m_dfg.opVertices());
|
||||
|
||||
// Check results for consistency
|
||||
if (VL_UNLIKELY(m_doExpensiveChecks)) {
|
||||
|
|
|
|||
|
|
@ -240,20 +240,18 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
// Convert the graph back to combinational assignments
|
||||
|
||||
// The graph must have been regularized, so we only need to render assignments
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
||||
if (!vtxp->isDrivenByDfg()) continue;
|
||||
if (!vtx.isDrivenByDfg()) continue;
|
||||
|
||||
// Render packed variable assignments
|
||||
if (const DfgVarPacked* const dfgVarp = vtxp->cast<DfgVarPacked>()) {
|
||||
if (const DfgVarPacked* const dfgVarp = vtx.cast<DfgVarPacked>()) {
|
||||
convertVarDriver(dfgVarp);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Render array variable assignments
|
||||
convertArrayDiver(vtxp->as<DfgVarArray>());
|
||||
convertArrayDiver(vtx.as<DfgVarArray>());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,29 +109,22 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
|
||||
// Pre-hash variables, these are all unique, so just set their hash to a unique value
|
||||
uint32_t varHash = 0;
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
vtxp->user<V3Hash>() = V3Hash{++varHash};
|
||||
}
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) vtx.user<V3Hash>() = V3Hash{++varHash};
|
||||
|
||||
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
|
||||
// expressions using the same constants to be combined, so we do need to hash equal
|
||||
// constants to equal values.
|
||||
for (DfgConst *vtxp = dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) {
|
||||
// Delete unused constants while we are at it.
|
||||
if (!vtxp->hasSinks()) {
|
||||
vtxp->unlinkDelete(dfg);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
continue;
|
||||
}
|
||||
vtxp->user<V3Hash>() = vtxp->num().toHash() + varHash;
|
||||
}
|
||||
|
||||
// Combine operation vertices
|
||||
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
for (DfgVertex* const vtxp : dfg.opVertices().unlinkable()) {
|
||||
// Delete unused nodes while we are at it.
|
||||
if (!vtxp->hasSinks()) {
|
||||
vtxp->unlinkDelete(dfg);
|
||||
|
|
@ -144,7 +137,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
if (candidatep->equals(*vtxp, equalsCache)) {
|
||||
++ctx.m_eliminated;
|
||||
vtxp->replaceWith(candidatep);
|
||||
vtxp->unlinkDelete(dfg);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -158,10 +151,9 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
removeUnused(dfg);
|
||||
}
|
||||
|
||||
void V3DfgPasses::inlineVars(const DfgGraph& dfg) {
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>()) {
|
||||
void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
|
||||
// Don't inline SystemC variables, as SystemC types are not interchangeable with
|
||||
// internal types, and hence the variables are not interchangeable either.
|
||||
if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) {
|
||||
|
|
@ -170,11 +162,11 @@ void V3DfgPasses::inlineVars(const DfgGraph& dfg) {
|
|||
// We must keep the original driver in certain cases, when swapping them would
|
||||
// not be functionally or technically (implementation reasons) equivalent
|
||||
if (DfgVertexVar* const driverVarp = driverp->cast<DfgVarPacked>()) {
|
||||
const AstVar* const varp = driverVarp->varp();
|
||||
const AstVar* const astVarp = driverVarp->varp();
|
||||
// If driven from a SystemC variable
|
||||
if (varp->isSc()) continue;
|
||||
if (astVarp->isSc()) continue;
|
||||
// If the variable is forceable
|
||||
if (varp->isForceable()) continue;
|
||||
if (astVarp->isForceable()) continue;
|
||||
}
|
||||
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) {
|
||||
|
|
@ -202,16 +194,14 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
DfgVertex* workListp = sentinelp;
|
||||
|
||||
// Add all unused vertices to the work list. This also allocates all DfgVertex::user.
|
||||
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
if (vtxp->hasSinks()) {
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.hasSinks()) {
|
||||
// This vertex is used. Allocate user, but don't add to work list.
|
||||
vtxp->setUser<DfgVertex*>(nullptr);
|
||||
vtx.setUser<DfgVertex*>(nullptr);
|
||||
} else {
|
||||
// This vertex is unused. Add to work list.
|
||||
vtxp->setUser<DfgVertex*>(workListp);
|
||||
workListp = vtxp;
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -240,9 +230,8 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
}
|
||||
|
||||
// Finally remove unused constants
|
||||
for (DfgConst *vtxp = dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (!vtxp->hasSinks()) vtxp->unlinkDelete(dfg);
|
||||
for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) {
|
||||
if (!vtxp->hasSinks()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -258,11 +247,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
DfgVertex* workListp = sentinelp;
|
||||
|
||||
// Add all variables to the initial work list
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
vtxp->setUser<DfgVertex*>(workListp);
|
||||
workListp = vtxp;
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
|
||||
const auto addToWorkList = [&](DfgVertex& vtx) {
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ AstModule* dfgToAst(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
|||
// Common subexpression elimination
|
||||
void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED;
|
||||
// Inline fully driven variables
|
||||
void inlineVars(const DfgGraph&) VL_MT_DISABLED;
|
||||
void inlineVars(DfgGraph&) VL_MT_DISABLED;
|
||||
// Peephole optimizations
|
||||
void peephole(DfgGraph&, V3DfgPeepholeContext&) VL_MT_DISABLED;
|
||||
// Regularize graph. This must be run before converting back to Ast.
|
||||
|
|
|
|||
|
|
@ -1533,12 +1533,10 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
// Add all vertices to the work list, and to the vertex cache.
|
||||
// This also allocates all DfgVertex::user.
|
||||
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
vtxp->setUser<DfgVertex*>(m_workListp);
|
||||
m_workListp = vtxp;
|
||||
m_cache.cache(vtxp);
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
vtx.setUser<DfgVertex*>(m_workListp);
|
||||
m_workListp = &vtx;
|
||||
m_cache.cache(&vtx);
|
||||
}
|
||||
|
||||
// Process the work list
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@ class DfgRegularize final {
|
|||
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
|
||||
|
||||
// Return canonical variable that can be used to hold the value of this vertex
|
||||
DfgVarPacked* getCanonicalVariable(DfgVertex* vtxp) {
|
||||
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
|
||||
// First gather all existing variables fully written by this vertex
|
||||
std::vector<DfgVarPacked*> varVtxps;
|
||||
vtxp->forEachSink([&](DfgVertex& vtx) {
|
||||
if (DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
vtx.forEachSink([&](DfgVertex& sink) {
|
||||
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp);
|
||||
}
|
||||
});
|
||||
|
|
@ -79,9 +79,9 @@ class DfgRegularize final {
|
|||
++m_ctx.m_temporariesIntroduced;
|
||||
|
||||
// Add temporary AstVar to containing module
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
FileLine* const flp = vtx.fileline();
|
||||
const std::string name = m_tmpNamePrefix + std::to_string(m_nTmps++);
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtxp->dtypep()};
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtx.dtypep()};
|
||||
m_dfg.modulep()->addStmtsp(varp);
|
||||
|
||||
// Create and return a variable vertex for the temporary
|
||||
|
|
@ -94,27 +94,25 @@ class DfgRegularize final {
|
|||
, m_ctx{ctx} {
|
||||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtxp->hasMultipleSinks()) continue;
|
||||
if (!vtx.hasMultipleSinks()) continue;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtxp->is<DfgArraySel>()) continue;
|
||||
if (vtx.is<DfgArraySel>()) continue;
|
||||
|
||||
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
|
||||
DfgVarPacked* const varp = getCanonicalVariable(vtxp);
|
||||
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
||||
if (varp->arity()) {
|
||||
// Existing variable
|
||||
FileLine* const flp = varp->driverFileLine(0);
|
||||
varp->sourceEdge(0)->unlinkSource();
|
||||
varp->resetSources();
|
||||
vtxp->replaceWith(varp);
|
||||
varp->addDriver(flp, 0, vtxp);
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(flp, 0, &vtx);
|
||||
} else {
|
||||
// Temporary variable
|
||||
vtxp->replaceWith(varp);
|
||||
varp->addDriver(vtxp->fileline(), 0, vtxp);
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(vtx.fileline(), 0, &vtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,13 +53,6 @@ public:
|
|||
, m_varp{varp} {}
|
||||
ASTGEN_MEMBERS_DfgVertexVar;
|
||||
|
||||
DfgVertexVar* verticesNext() const {
|
||||
return static_cast<DfgVertexVar*>(DfgVertex::verticesNext());
|
||||
}
|
||||
DfgVertexVar* verticesPrev() const {
|
||||
return static_cast<DfgVertexVar*>(DfgVertex::verticesPrev());
|
||||
}
|
||||
|
||||
bool isDrivenByDfg() const { return arity() > 0; }
|
||||
|
||||
AstVar* varp() const { return m_varp; }
|
||||
|
|
@ -107,9 +100,6 @@ public:
|
|||
, m_num{flp, static_cast<int>(width), value} {}
|
||||
ASTGEN_MEMBERS_DfgConst;
|
||||
|
||||
DfgConst* verticesNext() const { return static_cast<DfgConst*>(DfgVertex::verticesNext()); }
|
||||
DfgConst* verticesPrev() const { return static_cast<DfgConst*>(DfgVertex::verticesPrev()); }
|
||||
|
||||
V3Number& num() { return m_num; }
|
||||
const V3Number& num() const { return m_num; }
|
||||
|
||||
|
|
|
|||
|
|
@ -847,11 +847,10 @@ void EmitCSyms::emitSymImp() {
|
|||
puts("// Configure profiling for PGO\n");
|
||||
if (v3Global.opt.mtasks()) {
|
||||
v3Global.rootp()->topModulep()->foreach([&](const AstExecGraph* execGraphp) {
|
||||
for (const V3GraphVertex* vxp = execGraphp->depGraphp()->verticesBeginp(); vxp;
|
||||
vxp = vxp->verticesNextp()) {
|
||||
const ExecMTask* const mtp = static_cast<const ExecMTask*>(vxp);
|
||||
puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mtp->id()) + ", \""
|
||||
+ mtp->hashName() + "\");\n");
|
||||
for (const V3GraphVertex& vtx : execGraphp->depGraphp()->vertices()) {
|
||||
const ExecMTask& mt = static_cast<const ExecMTask&>(vtx);
|
||||
puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mt.id()) + ", \"" + mt.hashName()
|
||||
+ "\");\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ private:
|
|||
|
||||
// Find minimum cost MTask for scaling MTask node widths
|
||||
uint32_t minCost = UINT32_MAX;
|
||||
for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
if (const ExecMTask* const mtaskp = vxp->cast<const ExecMTask>()) {
|
||||
for (const V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (const ExecMTask* const mtaskp = vtx.cast<const ExecMTask>()) {
|
||||
minCost = minCost > mtaskp->cost() ? mtaskp->cost() : minCost;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,17 +142,17 @@ private:
|
|||
};
|
||||
|
||||
// Emit MTasks
|
||||
for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
if (const ExecMTask* const mtaskp = vxp->cast<const ExecMTask>()) emitMTask(mtaskp);
|
||||
for (const V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (const ExecMTask* const mtaskp = vtx.cast<const ExecMTask>()) emitMTask(mtaskp);
|
||||
}
|
||||
|
||||
// Emit MTask dependency edges
|
||||
*logp << "\n // MTask dependencies\n";
|
||||
for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
if (const ExecMTask* const mtaskp = vxp->cast<const ExecMTask>()) {
|
||||
for (V3GraphEdge* edgep = mtaskp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const V3GraphVertex* const top = edgep->top();
|
||||
*logp << " " << vxp->name() << " -> " << top->name() << "\n";
|
||||
for (const V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (const ExecMTask* const mtaskp = vtx.cast<const ExecMTask>()) {
|
||||
for (const V3GraphEdge& edge : mtaskp->outEdges()) {
|
||||
const V3GraphVertex* const top = edge.top();
|
||||
*logp << " " << vtx.name() << " -> " << top->name() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -173,8 +173,8 @@ public:
|
|||
uint32_t crossThreadDependencies(const ExecMTask* mtaskp) const {
|
||||
const uint32_t thisThreadId = threadId(mtaskp);
|
||||
uint32_t result = 0;
|
||||
for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
const ExecMTask* const prevp = edgep->fromp()->as<ExecMTask>();
|
||||
for (const V3GraphEdge& edge : mtaskp->inEdges()) {
|
||||
const ExecMTask* const prevp = edge.fromp()->as<ExecMTask>();
|
||||
if (threadId(prevp) != thisThreadId) ++result;
|
||||
}
|
||||
return result;
|
||||
|
|
@ -261,8 +261,8 @@ class PackThreads final {
|
|||
}
|
||||
|
||||
bool isReady(ThreadSchedule& schedule, const ExecMTask* mtaskp) {
|
||||
for (V3GraphEdge* edgeInp = mtaskp->inBeginp(); edgeInp; edgeInp = edgeInp->inNextp()) {
|
||||
const ExecMTask* const prevp = edgeInp->fromp()->as<const ExecMTask>();
|
||||
for (const V3GraphEdge& edgeIn : mtaskp->inEdges()) {
|
||||
const ExecMTask* const prevp = edgeIn.fromp()->as<const ExecMTask>();
|
||||
if (schedule.threadId(prevp) == ThreadSchedule::UNASSIGNED) {
|
||||
// This predecessor is not assigned yet
|
||||
return false;
|
||||
|
|
@ -272,7 +272,7 @@ class PackThreads final {
|
|||
}
|
||||
|
||||
// Pack an MTasks from given graph into m_nThreads threads, return the schedule.
|
||||
ThreadSchedule pack(const V3Graph& mtaskGraph) {
|
||||
ThreadSchedule pack(V3Graph& mtaskGraph) {
|
||||
// The result
|
||||
ThreadSchedule schedule{m_nThreads};
|
||||
|
||||
|
|
@ -283,8 +283,8 @@ class PackThreads final {
|
|||
std::set<ExecMTask*, MTaskCmp> readyMTasks;
|
||||
|
||||
// Build initial ready list
|
||||
for (V3GraphVertex* vxp = mtaskGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
ExecMTask* const mtaskp = vxp->as<ExecMTask>();
|
||||
for (V3GraphVertex& vtx : mtaskGraph.vertices()) {
|
||||
ExecMTask* const mtaskp = vtx.as<ExecMTask>();
|
||||
if (isReady(schedule, mtaskp)) readyMTasks.insert(mtaskp);
|
||||
}
|
||||
|
||||
|
|
@ -303,9 +303,8 @@ class PackThreads final {
|
|||
<< ", skipping thread.\n");
|
||||
break;
|
||||
}
|
||||
for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep;
|
||||
edgep = edgep->inNextp()) {
|
||||
const ExecMTask* const priorp = edgep->fromp()->as<ExecMTask>();
|
||||
for (const V3GraphEdge& edge : mtaskp->inEdges()) {
|
||||
const ExecMTask* const priorp = edge.fromp()->as<ExecMTask>();
|
||||
const uint32_t priorEndTime = completionTime(schedule, priorp, threadId);
|
||||
if (priorEndTime > timeBegin) timeBegin = priorEndTime;
|
||||
}
|
||||
|
|
@ -343,9 +342,8 @@ class PackThreads final {
|
|||
// Update the ready list
|
||||
const size_t erased = readyMTasks.erase(bestMtaskp);
|
||||
UASSERT_OBJ(erased > 0, bestMtaskp, "Should have erased something?");
|
||||
for (V3GraphEdge* edgeOutp = bestMtaskp->outBeginp(); edgeOutp;
|
||||
edgeOutp = edgeOutp->outNextp()) {
|
||||
ExecMTask* const nextp = edgeOutp->top()->as<ExecMTask>();
|
||||
for (V3GraphEdge& edgeOut : bestMtaskp->outEdges()) {
|
||||
ExecMTask* const nextp = edgeOut.top()->as<ExecMTask>();
|
||||
// Dependent MTask should not yet be assigned to a thread
|
||||
UASSERT(schedule.threadId(nextp) == ThreadSchedule::UNASSIGNED,
|
||||
"Tasks after one being assigned should not be assigned yet");
|
||||
|
|
@ -427,7 +425,7 @@ public:
|
|||
for (AstNode* const nodep : mTaskBodyps) nodep->deleteTree();
|
||||
}
|
||||
|
||||
static const ThreadSchedule apply(const V3Graph& mtaskGraph) {
|
||||
static const ThreadSchedule apply(V3Graph& mtaskGraph) {
|
||||
return PackThreads{}.pack(mtaskGraph);
|
||||
}
|
||||
};
|
||||
|
|
@ -493,11 +491,8 @@ void fillinCosts(V3Graph* execMTaskGraphp) {
|
|||
// Pass 1: See what profiling data applies
|
||||
Costs costs; // For each mtask, costs
|
||||
|
||||
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
|
||||
vxp = vxp->verticesNextp()) {
|
||||
ExecMTask* const mtp = const_cast<V3GraphVertex*>(vxp)->as<ExecMTask>();
|
||||
// Compute name of mtask, for hash lookup
|
||||
|
||||
for (V3GraphVertex& vtx : execMTaskGraphp->vertices()) {
|
||||
ExecMTask* const mtp = vtx.as<ExecMTask>();
|
||||
// This estimate is 64 bits, but the final mtask graph algorithm needs 32 bits
|
||||
const uint64_t costEstimate = V3InstrCount::count(mtp->bodyp(), false);
|
||||
const uint64_t costProfiled
|
||||
|
|
@ -513,9 +508,8 @@ void fillinCosts(V3Graph* execMTaskGraphp) {
|
|||
|
||||
int totalEstimates = 0;
|
||||
int missingProfiles = 0;
|
||||
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
|
||||
vxp = vxp->verticesNextp()) {
|
||||
ExecMTask* const mtp = const_cast<V3GraphVertex*>(vxp)->as<ExecMTask>();
|
||||
for (V3GraphVertex& vtx : execMTaskGraphp->vertices()) {
|
||||
ExecMTask* const mtp = vtx.as<ExecMTask>();
|
||||
const uint32_t costEstimate = costs[mtp->id()].first;
|
||||
const uint64_t costProfiled = costs[mtp->id()].second;
|
||||
UINFO(9, "ce = " << costEstimate << " cp=" << costProfiled << endl);
|
||||
|
|
@ -550,8 +544,8 @@ void finalizeCosts(V3Graph* execMTaskGraphp) {
|
|||
// choice among several ready mtasks, we'll want to start the
|
||||
// highest priority one first, so we're always working on the "long
|
||||
// pole"
|
||||
for (V3GraphEdge* edgep = mtp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const ExecMTask* const followp = edgep->top()->as<ExecMTask>();
|
||||
for (V3GraphEdge& edge : mtp->outEdges()) {
|
||||
const ExecMTask* const followp = edge.top()->as<ExecMTask>();
|
||||
if ((followp->priority() + mtp->cost()) > mtp->priority()) {
|
||||
mtp->priority(followp->priority() + mtp->cost());
|
||||
}
|
||||
|
|
@ -561,9 +555,8 @@ void finalizeCosts(V3Graph* execMTaskGraphp) {
|
|||
// Some MTasks may now have zero cost, eliminate those.
|
||||
// (It's common for tasks to shrink to nothing when V3LifePost
|
||||
// removes dly assignments.)
|
||||
for (V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;) {
|
||||
ExecMTask* const mtp = vxp->as<ExecMTask>();
|
||||
vxp = vxp->verticesNextp(); // Advance before delete
|
||||
for (V3GraphVertex* const vtxp : execMTaskGraphp->vertices().unlinkable()) {
|
||||
ExecMTask* const mtp = vtxp->as<ExecMTask>();
|
||||
|
||||
// Don't rely on checking mtp->cost() == 0 to detect an empty task.
|
||||
// Our cost-estimating logic is just an estimate. Instead, check
|
||||
|
|
@ -571,9 +564,9 @@ void finalizeCosts(V3Graph* execMTaskGraphp) {
|
|||
AstMTaskBody* const bodyp = mtp->bodyp();
|
||||
if (!bodyp->stmtsp()) { // Kill this empty mtask
|
||||
UINFO(6, "Removing zero-cost " << mtp->name() << endl);
|
||||
for (V3GraphEdge* inp = mtp->inBeginp(); inp; inp = inp->inNextp()) {
|
||||
for (V3GraphEdge* outp = mtp->outBeginp(); outp; outp = outp->outNextp()) {
|
||||
new V3GraphEdge{execMTaskGraphp, inp->fromp(), outp->top(), 1};
|
||||
for (V3GraphEdge& in : mtp->inEdges()) {
|
||||
for (V3GraphEdge& out : mtp->outEdges()) {
|
||||
new V3GraphEdge{execMTaskGraphp, in.fromp(), out.top(), 1};
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(mtp->unlinkDelete(execMTaskGraphp), mtp);
|
||||
|
|
@ -647,8 +640,8 @@ void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId,
|
|||
}
|
||||
|
||||
// For any dependent mtask that's on another thread, signal one dependency completion.
|
||||
for (V3GraphEdge* edgep = mtaskp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const ExecMTask* const nextp = edgep->top()->as<ExecMTask>();
|
||||
for (const V3GraphEdge& edge : mtaskp->outEdges()) {
|
||||
const ExecMTask* const nextp = edge.top()->as<ExecMTask>();
|
||||
if (schedule.threadId(nextp) != threadId) {
|
||||
addStrStmt("vlSelf->__Vm_mtaskstate_" + cvtToStr(nextp->id())
|
||||
+ ".signalUpstreamDone(even_cycle);\n");
|
||||
|
|
|
|||
102
src/V3Gate.cpp
102
src/V3Gate.cpp
|
|
@ -335,8 +335,8 @@ public:
|
|||
static void v3GateWarnSyncAsync(GateGraph& graph) {
|
||||
// AstVar::user2 -> bool: Warned about SYNCASYNCNET
|
||||
const VNUser2InUse user2InUse;
|
||||
for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (const GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
|
||||
for (V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (const GateVarVertex* const vvertexp = vtx.cast<GateVarVertex>()) {
|
||||
const AstVarScope* const vscp = vvertexp->varScp();
|
||||
const AstNode* const sp = vvertexp->rstSyncNodep();
|
||||
const AstNode* const ap = vvertexp->rstAsyncNodep();
|
||||
|
|
@ -438,9 +438,8 @@ class GateClkDecomp final {
|
|||
++m_seenClkVectors;
|
||||
}
|
||||
|
||||
for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
// Need to find the next edge before visiting in case the edge is deleted
|
||||
nextp = edgep->outNextp();
|
||||
// Edge might be deleted, so need unlinkable iteration
|
||||
for (V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) {
|
||||
visit(edgep->top()->as<GateLogicVertex>(), offset, vscp);
|
||||
}
|
||||
|
||||
|
|
@ -485,7 +484,7 @@ class GateClkDecomp final {
|
|||
"Should only make it here with clkOffset == 0");
|
||||
rhsp->replaceWith(
|
||||
new AstVarRef{rhsp->fileline(), m_clkVtxp->varScp(), VAccess::READ});
|
||||
while (V3GraphEdge* const edgep = lVtxp->inBeginp()) {
|
||||
while (V3GraphEdge* const edgep = lVtxp->inEdges().frontp()) {
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
}
|
||||
m_graph.addEdge(m_clkVtxp, lVtxp, 1);
|
||||
|
|
@ -495,14 +494,14 @@ class GateClkDecomp final {
|
|||
return;
|
||||
}
|
||||
|
||||
visit(lVtxp->outBeginp()->top()->as<GateVarVertex>(), clkOffset);
|
||||
visit(lVtxp->outEdges().frontp()->top()->as<GateVarVertex>(), clkOffset);
|
||||
}
|
||||
|
||||
explicit GateClkDecomp(GateGraph& graph)
|
||||
: m_graph{graph} {
|
||||
UINFO(9, "Starting clock decomposition" << endl);
|
||||
for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
GateVarVertex* const vVtxp = itp->cast<GateVarVertex>();
|
||||
for (V3GraphVertex& vtx : graph.vertices()) {
|
||||
GateVarVertex* const vVtxp = vtx.cast<GateVarVertex>();
|
||||
if (!vVtxp) continue;
|
||||
|
||||
const AstVarScope* const vscp = vVtxp->varScp();
|
||||
|
|
@ -740,17 +739,18 @@ class GateInline final {
|
|||
substitutions.clear();
|
||||
}
|
||||
|
||||
static GateVarVertex* ffToVarVtx(V3GraphVertex* vtxp) {
|
||||
while (vtxp && !vtxp->is<GateVarVertex>()) vtxp = vtxp->verticesNextp();
|
||||
return static_cast<GateVarVertex*>(vtxp);
|
||||
}
|
||||
|
||||
void optimizeSignals(bool allowMultiIn) {
|
||||
// Consider "inlining" variables
|
||||
GateVarVertex* nextp = ffToVarVtx(m_graph.verticesBeginp());
|
||||
while (GateVarVertex* const vVtxp = nextp) {
|
||||
auto& vertices = m_graph.vertices();
|
||||
const auto ffToVarVtx = [&](V3GraphVertex::List::iterator it) {
|
||||
while (it != vertices.end() && !(*it).is<GateVarVertex>()) ++it;
|
||||
return it;
|
||||
};
|
||||
V3GraphVertex::List::iterator vIt = ffToVarVtx(vertices.begin());
|
||||
while (vIt != vertices.end()) {
|
||||
GateVarVertex* const vVtxp = (*vIt).as<GateVarVertex>();
|
||||
// vVtxp and it's driving logic might be deleted, so grab next up front
|
||||
nextp = ffToVarVtx(vVtxp->verticesNextp());
|
||||
vIt = ffToVarVtx(++vIt);
|
||||
|
||||
// Nothing to inline if no driver, or multiple drivers ...
|
||||
if (!vVtxp->inSize1()) continue;
|
||||
|
|
@ -759,7 +759,8 @@ class GateInline final {
|
|||
if (!vVtxp->reducible()) continue;
|
||||
|
||||
// Grab the driving logic
|
||||
GateLogicVertex* const lVtxp = vVtxp->inBeginp()->fromp()->as<GateLogicVertex>();
|
||||
GateLogicVertex* const lVtxp
|
||||
= vVtxp->inEdges().frontp()->fromp()->as<GateLogicVertex>();
|
||||
if (!lVtxp->reducible()) continue;
|
||||
AstNode* const logicp = lVtxp->nodep();
|
||||
|
||||
|
|
@ -779,10 +780,10 @@ class GateInline final {
|
|||
} else {
|
||||
// Do it if not used, or used only once, ignoring slow code
|
||||
int n = 0;
|
||||
for (V3GraphEdge* ep = vVtxp->outBeginp(); ep; ep = ep->outNextp()) {
|
||||
const GateLogicVertex* const dstVtxp = ep->top()->as<GateLogicVertex>();
|
||||
for (V3GraphEdge& edge : vVtxp->outEdges()) {
|
||||
const GateLogicVertex* const dstVtxp = edge.top()->as<GateLogicVertex>();
|
||||
// Ignore slow code, or if the destination is not used
|
||||
if (!dstVtxp->slow() && dstVtxp->outBeginp()) n += ep->weight();
|
||||
if (!dstVtxp->slow() && !dstVtxp->outEmpty()) n += edge.weight();
|
||||
if (n > 1) break;
|
||||
}
|
||||
if (n > 1) continue;
|
||||
|
|
@ -803,18 +804,14 @@ class GateInline final {
|
|||
const std::unordered_set<AstVarScope*> readVscps{readVscpsVec.begin(),
|
||||
readVscpsVec.end()};
|
||||
|
||||
for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
// Edge might be deleted, so grab next
|
||||
nextp = edgep->outNextp();
|
||||
|
||||
for (V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) {
|
||||
GateLogicVertex* const dstVtxp = edgep->top()->as<GateLogicVertex>();
|
||||
|
||||
// If the consumer logic writes one of the variables that the substitution
|
||||
// is reading, then we would get a cycles, so we cannot do that.
|
||||
bool canInline = true;
|
||||
for (V3GraphEdge* dedgep = dstVtxp->outBeginp(); dedgep;
|
||||
dedgep = dedgep->outNextp()) {
|
||||
const GateVarVertex* const consVVertexp = dedgep->top()->as<GateVarVertex>();
|
||||
for (V3GraphEdge& dedge : dstVtxp->outEdges()) {
|
||||
const GateVarVertex* const consVVertexp = dedge.top()->as<GateVarVertex>();
|
||||
if (readVscps.count(consVVertexp->varScp())) {
|
||||
canInline = false;
|
||||
break;
|
||||
|
|
@ -1079,8 +1076,8 @@ class GateDedupe final {
|
|||
if (!vVtxp->inSize1()) return;
|
||||
|
||||
AstNodeVarRef* dupRefp = nullptr;
|
||||
for (V3GraphEdge* edgep = vVtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
dupRefp = visit(edgep->fromp()->as<GateLogicVertex>(), vVtxp);
|
||||
for (V3GraphEdge& edge : vVtxp->inEdges()) {
|
||||
dupRefp = visit(edge.fromp()->as<GateLogicVertex>(), vVtxp);
|
||||
}
|
||||
if (!dupRefp) return;
|
||||
|
||||
|
|
@ -1088,7 +1085,7 @@ class GateDedupe final {
|
|||
"GateLogicVertex* visit should have returned nullptr "
|
||||
"if consumer var vertex is not dedupable.");
|
||||
|
||||
GateLogicVertex* const lVtxp = vVtxp->inBeginp()->fromp()->as<GateLogicVertex>();
|
||||
GateLogicVertex* const lVtxp = vVtxp->inEdges().frontp()->fromp()->as<GateLogicVertex>();
|
||||
const GateOkVisitor okVisitor{lVtxp->nodep(), false, true};
|
||||
if (!okVisitor.isSimple()) return;
|
||||
|
||||
|
|
@ -1097,8 +1094,7 @@ class GateDedupe final {
|
|||
UINFO(4, "replacing " << vVtxp << " with " << dupVVtxp << endl);
|
||||
|
||||
// Replace all of this varvertex's consumers with dupRefp
|
||||
for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) {
|
||||
const GateLogicVertex* const consumerVtxp = edgep->top()->as<GateLogicVertex>();
|
||||
AstNode* const consumerp = consumerVtxp->nodep();
|
||||
UINFO(9, "elim src vtx" << lVtxp << " node " << lVtxp->nodep() << endl);
|
||||
|
|
@ -1130,7 +1126,7 @@ class GateDedupe final {
|
|||
}
|
||||
|
||||
// Remove inputs links
|
||||
while (V3GraphEdge* const edgep = vVtxp->inBeginp()) {
|
||||
while (V3GraphEdge* const edgep = vVtxp->inEdges().frontp()) {
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
}
|
||||
|
||||
|
|
@ -1141,9 +1137,7 @@ class GateDedupe final {
|
|||
// Given iterated logic, starting at consumerVtxp, returns a varref that
|
||||
// has the same logic input, or nullptr if none
|
||||
AstNodeVarRef* visit(GateLogicVertex* lVtxp, const GateVarVertex* consumerVtxp) {
|
||||
for (V3GraphEdge* edgep = lVtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
visit(edgep->fromp()->as<GateVarVertex>());
|
||||
}
|
||||
for (V3GraphEdge& edge : lVtxp->inEdges()) visit(edge.fromp()->as<GateVarVertex>());
|
||||
|
||||
if (lVtxp->dedupable() && consumerVtxp->dedupable()) {
|
||||
// TODO: Doing a simple pointer comparison of activep won't work
|
||||
|
|
@ -1158,15 +1152,15 @@ class GateDedupe final {
|
|||
explicit GateDedupe(GateGraph& graph) {
|
||||
// Traverse starting from each of the clocks
|
||||
UINFO(9, "Gate dedupe() clocks:\n");
|
||||
for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vVtxp = itp->cast<GateVarVertex>()) {
|
||||
for (V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (GateVarVertex* const vVtxp = vtx.cast<GateVarVertex>()) {
|
||||
if (vVtxp->isClock()) visit(vVtxp);
|
||||
}
|
||||
}
|
||||
// Traverse starting from each of the outputs
|
||||
UINFO(9, "Gate dedupe() outputs:\n");
|
||||
for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vVtxp = itp->cast<GateVarVertex>()) {
|
||||
for (V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (GateVarVertex* const vVtxp = vtx.cast<GateVarVertex>()) {
|
||||
if (vVtxp->isTop() && vVtxp->varScp()->varp()->isWritable()) visit(vVtxp);
|
||||
}
|
||||
}
|
||||
|
|
@ -1209,9 +1203,7 @@ class GateMergeAssignments final {
|
|||
GateLogicVertex* prevLVtxp = nullptr;
|
||||
AstNodeAssign* prevAssignp = nullptr;
|
||||
|
||||
for (V3GraphEdge *edgep = vVtxp->inBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->inNextp(); // fThe edge could be deleted
|
||||
|
||||
for (V3GraphEdge* const edgep : vVtxp->inEdges().unlinkable()) {
|
||||
GateLogicVertex* const lVtxp = edgep->fromp()->as<GateLogicVertex>();
|
||||
if (!lVtxp->outSize1()) continue;
|
||||
|
||||
|
|
@ -1251,7 +1243,7 @@ class GateMergeAssignments final {
|
|||
// Don't need to delete assignp, will be handled
|
||||
|
||||
// Update the graph
|
||||
while (V3GraphEdge* const iedgep = lVtxp->inBeginp()) {
|
||||
while (V3GraphEdge* const iedgep = lVtxp->inEdges().frontp()) {
|
||||
GateVarVertex* const fromVtxp = iedgep->fromp()->as<GateVarVertex>();
|
||||
m_graph.addEdge(fromVtxp, prevLVtxp, 1);
|
||||
VL_DO_DANGLING(iedgep->unlinkDelete(), iedgep);
|
||||
|
|
@ -1259,7 +1251,6 @@ class GateMergeAssignments final {
|
|||
|
||||
// Delete the out-edges of lVtxp (there is only one, we checked earlier)
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
|
||||
++m_statAssignMerged;
|
||||
} else {
|
||||
prevLVtxp = lVtxp;
|
||||
|
|
@ -1271,8 +1262,8 @@ class GateMergeAssignments final {
|
|||
explicit GateMergeAssignments(GateGraph& graph)
|
||||
: m_graph{graph} {
|
||||
UINFO(6, "mergeAssigns\n");
|
||||
for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vVtxp = itp->cast<GateVarVertex>()) process(vVtxp);
|
||||
for (V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (GateVarVertex* const vVtxp = vtx.cast<GateVarVertex>()) process(vVtxp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1298,8 +1289,8 @@ class GateUnused final {
|
|||
vtxp->user(true);
|
||||
vtxp->setConsumed("propagated");
|
||||
// Walk sources and mark them too
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
GateEitherVertex* const fromVtxp = static_cast<GateEitherVertex*>(edgep->fromp());
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
GateEitherVertex* const fromVtxp = static_cast<GateEitherVertex*>(edge.fromp());
|
||||
markRecurse(fromVtxp);
|
||||
}
|
||||
}
|
||||
|
|
@ -1307,9 +1298,9 @@ class GateUnused final {
|
|||
// Mark all vertices that feed a consumed vertex
|
||||
void mark() {
|
||||
m_graph.userClearVertices();
|
||||
for (V3GraphVertex* vtxp = m_graph.verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
GateEitherVertex* const eVtxp = static_cast<GateEitherVertex*>(vtxp);
|
||||
if (eVtxp->consumed()) markRecurse(eVtxp);
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
GateEitherVertex& eVtx = static_cast<GateEitherVertex&>(vtx);
|
||||
if (eVtx.consumed()) markRecurse(&eVtx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1327,8 +1318,7 @@ class GateUnused final {
|
|||
|
||||
// Remove unused logic
|
||||
void remove() {
|
||||
for (V3GraphVertex *vtxp = m_graph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNextp();
|
||||
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
||||
if (GateLogicVertex* const lVtxp = vtxp->cast<GateLogicVertex>()) {
|
||||
if (!lVtxp->consumed() && lVtxp->activep()) { // activep is nullptr under cfunc
|
||||
AstNode* const nodep = lVtxp->nodep();
|
||||
|
|
@ -1337,7 +1327,7 @@ class GateUnused final {
|
|||
UINFO(8, " Remove unconsumed " << nodep << endl);
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
lVtxp->unlinkDelete(&m_graph);
|
||||
VL_DO_DANGLING(lVtxp->unlinkDelete(&m_graph), lVtxp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
210
src/V3Graph.cpp
210
src/V3Graph.cpp
|
|
@ -41,7 +41,7 @@ V3GraphVertex::V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old)
|
|||
, m_color{old.m_color}
|
||||
, m_rank{old.m_rank} {
|
||||
m_userp = nullptr;
|
||||
verticesPushBack(graphp);
|
||||
graphp->vertices().linkBack(this);
|
||||
}
|
||||
|
||||
V3GraphVertex::V3GraphVertex(V3Graph* graphp)
|
||||
|
|
@ -49,67 +49,61 @@ V3GraphVertex::V3GraphVertex(V3Graph* graphp)
|
|||
, m_color{0}
|
||||
, m_rank{0} {
|
||||
m_userp = nullptr;
|
||||
verticesPushBack(graphp);
|
||||
}
|
||||
|
||||
void V3GraphVertex::verticesPushBack(V3Graph* graphp) {
|
||||
m_vertices.pushBack(graphp->m_vertices, this);
|
||||
graphp->vertices().linkBack(this);
|
||||
}
|
||||
|
||||
void V3GraphVertex::unlinkEdges(V3Graph*) {
|
||||
for (V3GraphEdge* edgep = outBeginp(); edgep; /*BELOW*/) {
|
||||
V3GraphEdge* nextp = edgep->outNextp();
|
||||
edgep->unlinkDelete();
|
||||
edgep = nextp;
|
||||
}
|
||||
for (V3GraphEdge* edgep = inBeginp(); edgep; /*BELOW*/) {
|
||||
V3GraphEdge* nextp = edgep->inNextp();
|
||||
edgep->unlinkDelete();
|
||||
edgep = nextp;
|
||||
}
|
||||
while (V3GraphEdge* const ep = m_outs.frontp()) VL_DO_DANGLING(ep->unlinkDelete(), ep);
|
||||
while (V3GraphEdge* const ep = m_ins.frontp()) VL_DO_DANGLING(ep->unlinkDelete(), ep);
|
||||
}
|
||||
|
||||
void V3GraphVertex::unlinkDelete(V3Graph* graphp) {
|
||||
// Delete edges
|
||||
unlinkEdges(graphp);
|
||||
// Unlink from vertex list
|
||||
m_vertices.unlink(graphp->m_vertices, this);
|
||||
graphp->m_vertices.unlink(this);
|
||||
// Delete
|
||||
delete this; // this=nullptr;
|
||||
}
|
||||
|
||||
void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
|
||||
// Make new edges for each from/to pair
|
||||
for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep = iedgep->inNextp()) {
|
||||
for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep = oedgep->outNextp()) {
|
||||
new V3GraphEdge{graphp, iedgep->fromp(), oedgep->top(),
|
||||
std::min(iedgep->weight(), oedgep->weight()),
|
||||
iedgep->cutable() && oedgep->cutable()};
|
||||
for (V3GraphEdge& iedge : inEdges()) {
|
||||
for (V3GraphEdge& oedge : outEdges()) {
|
||||
new V3GraphEdge{graphp, iedge.fromp(), oedge.top(),
|
||||
std::min(iedge.weight(), oedge.weight()),
|
||||
iedge.cutable() && oedge.cutable()};
|
||||
}
|
||||
}
|
||||
// Remove old edges
|
||||
unlinkEdges(graphp);
|
||||
}
|
||||
|
||||
bool V3GraphVertex::inSize1() const { return !inEmpty() && !inBeginp()->inNextp(); }
|
||||
bool V3GraphVertex::outSize1() const { return !outEmpty() && !outBeginp()->outNextp(); }
|
||||
|
||||
V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp) {
|
||||
template <GraphWay::en T_Way>
|
||||
V3GraphEdge* V3GraphVertex::findConnectingEdgep(V3GraphVertex* waywardp) {
|
||||
// O(edges) linear search. Searches search both nodes' edge lists in
|
||||
// parallel. The lists probably aren't _both_ huge, so this is
|
||||
// unlikely to blow up even on fairly nasty graphs.
|
||||
const GraphWay inv = way.invert();
|
||||
V3GraphEdge* aedgep = this->beginp(way);
|
||||
V3GraphEdge* bedgep = waywardp->beginp(inv);
|
||||
while (aedgep && bedgep) {
|
||||
if (aedgep->furtherp(way) == waywardp) return aedgep;
|
||||
if (bedgep->furtherp(inv) == this) return bedgep;
|
||||
aedgep = aedgep->nextp(way);
|
||||
bedgep = bedgep->nextp(inv);
|
||||
constexpr GraphWay way{T_Way};
|
||||
constexpr GraphWay inv = way.invert();
|
||||
auto& aEdges = this->edges<way>();
|
||||
auto aIt = aEdges.begin();
|
||||
auto aEnd = aEdges.end();
|
||||
auto& bEdges = waywardp->edges<inv>();
|
||||
auto bIt = bEdges.begin();
|
||||
auto bEnd = bEdges.end();
|
||||
while (aIt != aEnd && bIt != bEnd) {
|
||||
V3GraphEdge& aedge = *aIt++;
|
||||
if (aedge.furtherp<way>() == waywardp) return &aedge;
|
||||
V3GraphEdge& bedge = *bIt++;
|
||||
if (bedge.furtherp<inv>() == this) return &bedge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template V3GraphEdge* V3GraphVertex::findConnectingEdgep<GraphWay::FORWARD>(V3GraphVertex*);
|
||||
template V3GraphEdge* V3GraphVertex::findConnectingEdgep<GraphWay::REVERSE>(V3GraphVertex*);
|
||||
|
||||
// cppcheck-has-bug-suppress constParameter
|
||||
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
std::ostringstream nsstr;
|
||||
|
|
@ -153,43 +147,36 @@ void V3GraphEdge::init(V3Graph* /*graphp*/, V3GraphVertex* fromp, V3GraphVertex*
|
|||
m_cutable = cutable;
|
||||
m_userp = nullptr;
|
||||
// Link vertices to this edge
|
||||
outPushBack();
|
||||
inPushBack();
|
||||
m_fromp->outEdges().linkBack(this);
|
||||
m_top->inEdges().linkBack(this);
|
||||
}
|
||||
|
||||
V3GraphEdge* V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) {
|
||||
V3GraphEdge* oldNxt = outNextp();
|
||||
m_outs.unlink(m_fromp->m_outs, this);
|
||||
void V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) {
|
||||
m_fromp->outEdges().unlink(this);
|
||||
m_fromp = newFromp;
|
||||
outPushBack();
|
||||
return oldNxt;
|
||||
m_fromp->outEdges().linkBack(this);
|
||||
}
|
||||
|
||||
V3GraphEdge* V3GraphEdge::relinkTop(V3GraphVertex* newTop) {
|
||||
V3GraphEdge* oldNxt = inNextp();
|
||||
m_ins.unlink(m_top->m_ins, this);
|
||||
void V3GraphEdge::relinkTop(V3GraphVertex* newTop) {
|
||||
m_top->inEdges().unlink(this);
|
||||
m_top = newTop;
|
||||
inPushBack();
|
||||
return oldNxt;
|
||||
m_top->inEdges().linkBack(this);
|
||||
}
|
||||
|
||||
void V3GraphEdge::unlinkDelete() {
|
||||
// Unlink from side
|
||||
m_outs.unlink(m_fromp->m_outs, this);
|
||||
m_fromp->outEdges().unlink(this);
|
||||
// Unlink to side
|
||||
m_ins.unlink(m_top->m_ins, this);
|
||||
m_top->inEdges().unlink(this);
|
||||
// Delete
|
||||
delete this; // this=nullptr;
|
||||
delete this;
|
||||
}
|
||||
|
||||
void V3GraphEdge::outPushBack() {
|
||||
// m_fromp->m_outsp.push_back(this);
|
||||
m_outs.pushBack(m_fromp->m_outs, this);
|
||||
}
|
||||
std::string V3GraphEdge::name() const { return m_fromp->name() + "->" + m_top->name(); }
|
||||
|
||||
void V3GraphEdge::inPushBack() {
|
||||
// m_top->m_insp.push_back(this);
|
||||
m_ins.pushBack(m_top->m_ins, this);
|
||||
int V3GraphEdge::sortCmp(const V3GraphEdge* rhsp) const {
|
||||
if (!m_weight || !rhsp->m_weight) return 0;
|
||||
return top()->sortCmp(rhsp->top());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -197,61 +184,50 @@ void V3GraphEdge::inPushBack() {
|
|||
// Graph top level
|
||||
// Constructors
|
||||
|
||||
V3Graph::V3Graph() {
|
||||
// Anything here is probably needed in clear() also
|
||||
verticesUnlink();
|
||||
}
|
||||
V3Graph::V3Graph() {}
|
||||
|
||||
V3Graph::~V3Graph() { clear(); }
|
||||
|
||||
void V3Graph::clear() {
|
||||
// Empty it of all points, as if making a new object
|
||||
// Delete the old edges
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; /*BELOW*/) {
|
||||
V3GraphEdge* nextp = edgep->outNextp();
|
||||
for (V3GraphVertex& vertex : vertices()) {
|
||||
while (V3GraphEdge* const edgep = vertex.outEdges().unlinkFront()) {
|
||||
VL_DO_DANGLING(delete edgep, edgep);
|
||||
edgep = nextp;
|
||||
}
|
||||
vertexp->outUnlink();
|
||||
}
|
||||
// Delete the old vertices
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; /*BELOW*/) {
|
||||
V3GraphVertex* nextp = vertexp->verticesNextp();
|
||||
while (V3GraphVertex* const vertexp = vertices().unlinkFront()) {
|
||||
VL_DO_DANGLING(delete vertexp, vertexp);
|
||||
vertexp = nextp;
|
||||
}
|
||||
verticesUnlink();
|
||||
}
|
||||
|
||||
void V3Graph::userClearVertices() {
|
||||
// Clear user() in all of tree
|
||||
// We may use the userCnt trick in V3Ast later... (but gblCnt would be
|
||||
// in V3Graph instead of static - which has the complication of finding
|
||||
// the graph pointer given a vertex.) For now we don't call this often, and
|
||||
// the graph pointer given a vertex.) For now, we don't call this often, and
|
||||
// the extra code on each read of user() would probably slow things
|
||||
// down more than help.
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
vertexp->user(0);
|
||||
vertexp->userp(nullptr); // Its a union, but might be different size than user()
|
||||
for (V3GraphVertex& vertex : vertices()) {
|
||||
vertex.user(0);
|
||||
vertex.userp(nullptr); // It's a union, but might be different size than user()
|
||||
}
|
||||
}
|
||||
|
||||
void V3Graph::userClearEdges() {
|
||||
// Clear user() in all of tree
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
edgep->user(0);
|
||||
edgep->userp(nullptr); // Its a union, but might be different size than user()
|
||||
for (V3GraphVertex& vertex : vertices()) {
|
||||
for (V3GraphEdge& edge : vertex.outEdges()) {
|
||||
edge.user(0);
|
||||
edge.userp(nullptr); // It's a union, but might be different size than user()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V3Graph::clearColors() {
|
||||
// Reset colors
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
vertexp->m_color = 0;
|
||||
}
|
||||
for (V3GraphVertex& vertex : vertices()) vertex.color(0);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
|
@ -270,35 +246,29 @@ void V3Graph::dump(std::ostream& os) const {
|
|||
// This generates a file used by graphviz, https://www.graphviz.org
|
||||
os << " Graph:\n";
|
||||
// Print vertices
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
os << "\tNode: " << vertexp->name();
|
||||
if (vertexp->color()) os << " color=" << vertexp->color();
|
||||
for (const V3GraphVertex& vertex : vertices()) {
|
||||
os << "\tNode: " << vertex.name();
|
||||
if (vertex.color()) os << " color=" << vertex.color();
|
||||
os << '\n';
|
||||
// Print edges
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
dumpEdge(os, vertexp, edgep);
|
||||
}
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
dumpEdge(os, vertexp, edgep);
|
||||
}
|
||||
for (const V3GraphEdge& edge : vertex.inEdges()) dumpEdge(os, vertex, edge);
|
||||
for (const V3GraphEdge& edge : vertex.outEdges()) dumpEdge(os, vertex, edge);
|
||||
}
|
||||
}
|
||||
|
||||
void V3Graph::dumpEdge(std::ostream& os, const V3GraphVertex* vertexp,
|
||||
const V3GraphEdge* edgep) const {
|
||||
if (edgep->weight() && (edgep->fromp() == vertexp || edgep->top() == vertexp)) {
|
||||
void V3Graph::dumpEdge(std::ostream& os, const V3GraphVertex& vertex,
|
||||
const V3GraphEdge& edge) const {
|
||||
if (edge.weight() && (edge.fromp() == &vertex || edge.top() == &vertex)) {
|
||||
os << "\t\t";
|
||||
if (edgep->fromp() == vertexp) os << "-> " << edgep->top()->name();
|
||||
if (edgep->top() == vertexp) os << "<- " << edgep->fromp()->name();
|
||||
if (edgep->cutable()) os << " [CUTABLE]";
|
||||
if (edge.fromp() == &vertex) os << "-> " << edge.top()->name();
|
||||
if (edge.top() == &vertex) os << "<- " << edge.fromp()->name();
|
||||
if (edge.cutable()) os << " [CUTABLE]";
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
void V3Graph::dumpEdges(std::ostream& os, const V3GraphVertex* vertexp) const {
|
||||
for (const V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp())
|
||||
dumpEdge(os, vertexp, edgep);
|
||||
for (const V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp())
|
||||
dumpEdge(os, vertexp, edgep);
|
||||
void V3Graph::dumpEdges(std::ostream& os, const V3GraphVertex& vertex) const {
|
||||
for (const V3GraphEdge& edge : vertex.inEdges()) dumpEdge(os, vertex, edge);
|
||||
for (const V3GraphEdge& edge : vertex.outEdges()) dumpEdge(os, vertex, edge);
|
||||
}
|
||||
|
||||
void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph) const {
|
||||
|
|
@ -326,16 +296,16 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
|
|||
// List of all possible subgraphs
|
||||
// Collections of explicit ranks
|
||||
std::unordered_set<std::string> ranks;
|
||||
std::unordered_multimap<std::string, V3GraphVertex*> rankSets;
|
||||
std::multimap<std::string, V3GraphVertex*> subgraphs;
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
std::unordered_multimap<std::string, const V3GraphVertex*> rankSets;
|
||||
std::multimap<std::string, const V3GraphVertex*> subgraphs;
|
||||
for (const V3GraphVertex& vertex : vertices()) {
|
||||
const string vertexSubgraph
|
||||
= (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
|
||||
subgraphs.emplace(vertexSubgraph, vertexp);
|
||||
const string& dotRank = vertexp->dotRank();
|
||||
= (colorAsSubgraph && vertex.color()) ? cvtToStr(vertex.color()) : "";
|
||||
subgraphs.emplace(vertexSubgraph, &vertex);
|
||||
const string& dotRank = vertex.dotRank();
|
||||
if (!dotRank.empty()) {
|
||||
ranks.emplace(dotRank);
|
||||
rankSets.emplace(dotRank, vertexp);
|
||||
rankSets.emplace(dotRank, &vertex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -373,20 +343,20 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
|
|||
if (subgr != "") *logp << "\t};\n";
|
||||
|
||||
// Print edges
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight()) {
|
||||
const int fromVnum = numMap[edgep->fromp()];
|
||||
const int toVnum = numMap[edgep->top()];
|
||||
*logp << "\tn" << edgep->fromp()->dotName() << fromVnum << " -> n"
|
||||
<< edgep->top()->dotName() << toVnum
|
||||
for (const V3GraphVertex& vertex : vertices()) {
|
||||
for (const V3GraphEdge& edge : vertex.outEdges()) {
|
||||
if (edge.weight()) {
|
||||
const int fromVnum = numMap[edge.fromp()];
|
||||
const int toVnum = numMap[edge.top()];
|
||||
*logp << "\tn" << edge.fromp()->dotName() << fromVnum << " -> n"
|
||||
<< edge.top()->dotName() << toVnum
|
||||
<< " ["
|
||||
// <<"fontsize=8 label=\""<<(edgep->name()!="" ? edgep->name() : "\\E")<<"\""
|
||||
<< "fontsize=8 label=\""
|
||||
<< (edgep->dotLabel() != "" ? edgep->dotLabel() : "") << "\""
|
||||
<< " weight=" << edgep->weight() << " color=" << edgep->dotColor();
|
||||
if (edgep->dotStyle() != "") *logp << " style=" << edgep->dotStyle();
|
||||
// if (edgep->cutable()) *logp << ",constraint=false"; // to rank without
|
||||
// <<"fontsize=8 label=\""<<(edge.name()!="" ? edge.name() : "\\E")<<"\""
|
||||
<< "fontsize=8 label=\"" << (edge.dotLabel() != "" ? edge.dotLabel() : "")
|
||||
<< "\""
|
||||
<< " weight=" << edge.weight() << " color=" << edge.dotColor();
|
||||
if (edge.dotStyle() != "") *logp << " style=" << edge.dotStyle();
|
||||
// if (edge.cutable()) *logp << ",constraint=false"; // to rank without
|
||||
// following edges
|
||||
*logp << "];\n";
|
||||
}
|
||||
|
|
|
|||
526
src/V3Graph.h
526
src/V3Graph.h
|
|
@ -78,11 +78,280 @@ constexpr bool operator==(const GraphWay& lhs, const GraphWay& rhs) { return lhs
|
|||
constexpr bool operator==(const GraphWay& lhs, GraphWay::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(GraphWay::en lhs, const GraphWay& rhs) { return lhs == rhs.m_e; }
|
||||
|
||||
//============================================================================
|
||||
// TODO should we have a smaller edge structure when we don't need weight etc?
|
||||
|
||||
class V3GraphEdge VL_NOT_FINAL {
|
||||
VL_RTTI_IMPL_BASE(V3GraphEdge)
|
||||
// Wires/variables aren't edges. Edges have only a single to/from vertex
|
||||
public:
|
||||
// ENUMS
|
||||
enum Cutable : uint8_t { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge
|
||||
protected:
|
||||
friend class V3Graph;
|
||||
friend class V3GraphVertex;
|
||||
friend class GraphAcyc;
|
||||
friend class GraphAcycEdge;
|
||||
|
||||
V3ListLinks<V3GraphEdge> m_oLinks; // List links to store instances of this class (out edges)
|
||||
V3ListLinks<V3GraphEdge> m_iLinks; // List links to store instances of this class (in edges)
|
||||
//
|
||||
V3GraphVertex* m_fromp; // Vertices pointing to this edge
|
||||
V3GraphVertex* m_top; // Vertices this edge points to
|
||||
int m_weight; // Weight of the connection
|
||||
bool m_cutable; // Interconnect may be broken in order sorting
|
||||
union {
|
||||
void* m_userp; // Marker for some algorithms
|
||||
uint64_t m_user; // Marker for some algorithms
|
||||
};
|
||||
// METHODS
|
||||
void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
|
||||
bool cutable = false) VL_MT_DISABLED;
|
||||
void cut() { m_weight = 0; } // 0 weight is same as disconnected
|
||||
// CONSTRUCTORS
|
||||
protected:
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const V3GraphEdge& old) VL_MT_DISABLED {
|
||||
init(graphp, fromp, top, old.m_weight, old.m_cutable);
|
||||
}
|
||||
|
||||
private:
|
||||
V3ListLinks<V3GraphEdge>& oLinks() { return m_oLinks; }
|
||||
V3ListLinks<V3GraphEdge>& iLinks() { return m_iLinks; }
|
||||
|
||||
public:
|
||||
// List types to store instances of this class
|
||||
using OList = V3List<V3GraphEdge, &V3GraphEdge::oLinks>;
|
||||
using IList = V3List<V3GraphEdge, &V3GraphEdge::iLinks>;
|
||||
|
||||
//! Add DAG from one node to the specified node
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
|
||||
bool cutable = false) VL_MT_DISABLED {
|
||||
init(graphp, fromp, top, weight, cutable);
|
||||
}
|
||||
//! Clone copy constructor. Doesn't copy existing vertices or user/userp.
|
||||
virtual V3GraphEdge* clone(V3Graph* graphp, V3GraphVertex* fromp,
|
||||
V3GraphVertex* top) const VL_MT_DISABLED {
|
||||
return new V3GraphEdge{graphp, fromp, top, *this};
|
||||
}
|
||||
virtual ~V3GraphEdge() = default;
|
||||
// METHODS
|
||||
// Return true iff of type T
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
static_assert(std::is_base_of<V3GraphEdge, T>::value,
|
||||
"'T' must be a subtype of V3GraphEdge");
|
||||
static_assert(std::is_same<typename std::remove_cv<T>::type,
|
||||
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
|
||||
"Missing VL_RTTI_IMPL(...) call in 'T'");
|
||||
return this->isInstanceOfClassWithId(T::rttiClassId());
|
||||
}
|
||||
|
||||
// Return cast to subtype T and assert of that type
|
||||
template <typename T>
|
||||
T* as() {
|
||||
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
template <typename T>
|
||||
const T* as() const {
|
||||
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
// Return cast to subtype T, else nullptr if different type
|
||||
template <typename T>
|
||||
T* cast() {
|
||||
return is<T>() ? static_cast<T*>(this) : nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
const T* cast() const {
|
||||
return is<T>() ? static_cast<const T*>(this) : nullptr;
|
||||
}
|
||||
|
||||
virtual string name() const VL_MT_DISABLED;
|
||||
virtual string dotLabel() const { return ""; }
|
||||
virtual string dotColor() const { return cutable() ? "yellowGreen" : "red"; }
|
||||
virtual string dotStyle() const { return cutable() ? "dashed" : ""; }
|
||||
virtual int sortCmp(const V3GraphEdge* rhsp) const VL_MT_DISABLED;
|
||||
void unlinkDelete() VL_MT_DISABLED;
|
||||
void relinkFromp(V3GraphVertex* newFromp) VL_MT_DISABLED;
|
||||
void relinkTop(V3GraphVertex* newTop) VL_MT_DISABLED;
|
||||
// ACCESSORS
|
||||
int weight() const { return m_weight; }
|
||||
void weight(int weight) { m_weight = weight; }
|
||||
bool cutable() const { return m_cutable; }
|
||||
void cutable(bool cutable) { m_cutable = cutable; }
|
||||
void userp(void* user) { m_userp = user; }
|
||||
void* userp() const { return m_userp; }
|
||||
void user(uint64_t user) { m_user = user; }
|
||||
uint64_t user() const { return m_user; }
|
||||
V3GraphVertex* fromp() const { return m_fromp; }
|
||||
V3GraphVertex* top() const { return m_top; }
|
||||
template <GraphWay::en T_Way>
|
||||
V3GraphVertex* furtherp() const {
|
||||
return T_Way == GraphWay::FORWARD ? top() : fromp();
|
||||
}
|
||||
// STATIC ACCESSORS
|
||||
static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; }
|
||||
static bool followAlwaysTrue(const V3GraphEdge*) { return true; }
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3GraphVertex VL_NOT_FINAL {
|
||||
VL_RTTI_IMPL_BASE(V3GraphVertex)
|
||||
// Vertices may be a 'gate'/wire statement OR a variable
|
||||
protected:
|
||||
friend class V3Graph;
|
||||
friend class V3GraphEdge;
|
||||
friend class GraphAcyc;
|
||||
friend class GraphAlgRank;
|
||||
V3ListLinks<V3GraphVertex> m_links; // List links to store instances of this class
|
||||
V3GraphEdge::OList m_outs; // List of outbound edges
|
||||
V3GraphEdge::IList m_ins; // List of inbound edges
|
||||
double m_fanout; // Order fanout
|
||||
uint32_t m_color; // Color of the node
|
||||
uint32_t m_rank; // Rank of edge
|
||||
union {
|
||||
void* m_userp; // Marker for some algorithms
|
||||
uint32_t m_user; // Marker for some algorithms
|
||||
};
|
||||
// ACCESSORS
|
||||
void fanout(double fanout) { m_fanout = fanout; }
|
||||
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old) VL_MT_DISABLED;
|
||||
|
||||
private:
|
||||
V3ListLinks<V3GraphVertex>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List types to store instances of this class
|
||||
using List = V3List<V3GraphVertex, &V3GraphVertex::links>;
|
||||
|
||||
explicit V3GraphVertex(V3Graph* graphp) VL_MT_DISABLED;
|
||||
//! Clone copy constructor. Doesn't copy edges or user/userp.
|
||||
virtual V3GraphVertex* clone(V3Graph* graphp) const VL_MT_DISABLED {
|
||||
return new V3GraphVertex{graphp, *this};
|
||||
}
|
||||
virtual ~V3GraphVertex() = default;
|
||||
void unlinkEdges(V3Graph* graphp) VL_MT_DISABLED;
|
||||
void unlinkDelete(V3Graph* graphp) VL_MT_DISABLED;
|
||||
|
||||
// METHODS
|
||||
// Return true iff of type T
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
static_assert(std::is_base_of<V3GraphVertex, T>::value,
|
||||
"'T' must be a subtype of V3GraphVertex");
|
||||
static_assert(std::is_same<typename std::remove_cv<T>::type,
|
||||
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
|
||||
"Missing VL_RTTI_IMPL(...) call in 'T'");
|
||||
return this->isInstanceOfClassWithId(T::rttiClassId());
|
||||
}
|
||||
|
||||
// Return cast to subtype T and assert of that type
|
||||
template <typename T>
|
||||
T* as() {
|
||||
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
template <typename T>
|
||||
const T* as() const {
|
||||
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
// Return cast to subtype T, else nullptr if different type
|
||||
template <typename T>
|
||||
T* cast() {
|
||||
return is<T>() ? static_cast<T*>(this) : nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
const T* cast() const {
|
||||
return is<T>() ? static_cast<const T*>(this) : nullptr;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const { return ""; }
|
||||
virtual string dotColor() const { return "black"; }
|
||||
virtual string dotShape() const { return ""; }
|
||||
virtual string dotStyle() const { return ""; }
|
||||
virtual string dotName() const { return ""; }
|
||||
virtual string dotRank() const { return ""; }
|
||||
virtual uint32_t rankAdder() const { return 1; }
|
||||
virtual FileLine* fileline() const { return nullptr; } // nullptr for unknown
|
||||
virtual int sortCmp(const V3GraphVertex* rhsp) const {
|
||||
// LHS goes first if of lower rank, or lower fanout
|
||||
if (m_rank < rhsp->m_rank) return -1;
|
||||
if (m_rank > rhsp->m_rank) return 1;
|
||||
if (m_fanout < rhsp->m_fanout) return -1;
|
||||
if (m_fanout > rhsp->m_fanout) return 1;
|
||||
return 0;
|
||||
}
|
||||
uint32_t color() const { return m_color; }
|
||||
void color(uint32_t color) { m_color = color; }
|
||||
uint32_t rank() const { return m_rank; }
|
||||
void rank(uint32_t rank) { m_rank = rank; }
|
||||
double fanout() const { return m_fanout; }
|
||||
void user(uint32_t user) { m_user = user; }
|
||||
uint32_t user() const VL_MT_STABLE { return m_user; }
|
||||
void userp(void* userp) { m_userp = userp; }
|
||||
void* userp() const VL_MT_STABLE { return m_userp; }
|
||||
V3GraphEdge::IList& inEdges() { return m_ins; }
|
||||
const V3GraphEdge::IList& inEdges() const { return m_ins; }
|
||||
template <GraphWay::en T_Way>
|
||||
inline auto& edges();
|
||||
template <GraphWay::en T_Way>
|
||||
inline const auto& edges() const;
|
||||
bool inEmpty() const { return m_ins.empty(); }
|
||||
bool inSize1() const { return m_ins.hasSingleElement(); }
|
||||
V3GraphEdge::OList& outEdges() { return m_outs; }
|
||||
const V3GraphEdge::OList& outEdges() const { return m_outs; }
|
||||
bool outEmpty() const { return m_outs.empty(); }
|
||||
bool outSize1() const { return m_outs.hasSingleElement(); }
|
||||
// METHODS
|
||||
/// Error reporting
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED;
|
||||
void v3errorEndFatal(std::ostringstream& str) const
|
||||
VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED;
|
||||
/// Edges are routed around this vertex to point from "from" directly to "to"
|
||||
void rerouteEdges(V3Graph* graphp) VL_MT_DISABLED;
|
||||
// Find the edge connecting this vertex to the given vertex.
|
||||
// If edge is not found returns nullptr. O(edges) performance.
|
||||
template <GraphWay::en T_Way>
|
||||
V3GraphEdge* findConnectingEdgep(V3GraphVertex* otherp) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) VL_MT_DISABLED;
|
||||
|
||||
template <>
|
||||
inline auto& V3GraphVertex::edges<GraphWay::FORWARD>() {
|
||||
return m_outs;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline auto& V3GraphVertex::edges<GraphWay::REVERSE>() {
|
||||
return m_ins;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline const auto& V3GraphVertex::edges<GraphWay::FORWARD>() const {
|
||||
return m_outs;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline const auto& V3GraphVertex::edges<GraphWay::REVERSE>() const {
|
||||
return m_ins;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Graph VL_NOT_FINAL {
|
||||
// MEMBERS
|
||||
V3List<V3GraphVertex*> m_vertices; // All vertices
|
||||
V3GraphVertex::List m_vertices; // All vertices
|
||||
|
||||
protected:
|
||||
friend class V3GraphVertex;
|
||||
|
|
@ -90,9 +359,8 @@ protected:
|
|||
friend class GraphAcyc;
|
||||
// METHODS
|
||||
double orderDFSIterate(V3GraphVertex* vertexp) VL_MT_DISABLED;
|
||||
void dumpEdge(std::ostream& os, const V3GraphVertex* vertexp,
|
||||
const V3GraphEdge* edgep) const VL_MT_DISABLED;
|
||||
void verticesUnlink() { m_vertices.reset(); }
|
||||
void dumpEdge(std::ostream& os, const V3GraphVertex& vertex,
|
||||
const V3GraphEdge& edge) const VL_MT_DISABLED;
|
||||
// ACCESSORS
|
||||
|
||||
public:
|
||||
|
|
@ -103,7 +371,8 @@ public:
|
|||
// METHODS
|
||||
void clear() VL_MT_DISABLED; // Empty it of all vertices/edges, as if making a new object
|
||||
bool empty() const { return m_vertices.empty(); }
|
||||
V3GraphVertex* verticesBeginp() const { return m_vertices.begin(); }
|
||||
V3GraphVertex::List& vertices() { return m_vertices; }
|
||||
const V3GraphVertex::List& vertices() const { return m_vertices; }
|
||||
|
||||
// METHODS - ALGORITHMS
|
||||
|
||||
|
|
@ -180,7 +449,7 @@ public:
|
|||
bool colorAsSubgraph = false) const VL_MT_DISABLED;
|
||||
void dumpDotFilePrefixedAlways(const string& nameComment,
|
||||
bool colorAsSubgraph = false) const VL_MT_DISABLED;
|
||||
void dumpEdges(std::ostream& os, const V3GraphVertex* vertexp) const VL_MT_DISABLED;
|
||||
void dumpEdges(std::ostream& os, const V3GraphVertex& vertex) const VL_MT_DISABLED;
|
||||
static void selfTest() VL_MT_DISABLED;
|
||||
|
||||
class ParallelismReport final {
|
||||
|
|
@ -212,8 +481,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
ParallelismReport parallelismReport(
|
||||
std::function<uint64_t(const V3GraphVertex*)> vertexCost) const VL_MT_DISABLED;
|
||||
ParallelismReport
|
||||
parallelismReport(std::function<uint64_t(const V3GraphVertex*)> vertexCost) VL_MT_DISABLED;
|
||||
|
||||
// CALLBACKS
|
||||
virtual void loopsMessageCb(V3GraphVertex* vertexp) VL_MT_DISABLED;
|
||||
|
|
@ -222,245 +491,4 @@ public:
|
|||
|
||||
//============================================================================
|
||||
|
||||
class V3GraphVertex VL_NOT_FINAL {
|
||||
VL_RTTI_IMPL_BASE(V3GraphVertex)
|
||||
// Vertices may be a 'gate'/wire statement OR a variable
|
||||
protected:
|
||||
friend class V3Graph;
|
||||
friend class V3GraphEdge;
|
||||
friend class GraphAcyc;
|
||||
friend class GraphAlgRank;
|
||||
V3ListEnt<V3GraphVertex*> m_vertices; // All vertices, linked list
|
||||
V3List<V3GraphEdge*> m_outs; // Outbound edges,linked list
|
||||
V3List<V3GraphEdge*> m_ins; // Inbound edges, linked list
|
||||
double m_fanout; // Order fanout
|
||||
uint32_t m_color; // Color of the node
|
||||
uint32_t m_rank; // Rank of edge
|
||||
union {
|
||||
void* m_userp; // Marker for some algorithms
|
||||
uint32_t m_user; // Marker for some algorithms
|
||||
};
|
||||
// METHODS
|
||||
void verticesPushBack(V3Graph* graphp) VL_MT_DISABLED;
|
||||
// ACCESSORS
|
||||
void fanout(double fanout) { m_fanout = fanout; }
|
||||
void inUnlink() { m_ins.reset(); } // Low level; normally unlinkDelete is what you want
|
||||
void outUnlink() { m_outs.reset(); } // Low level; normally unlinkDelete is what you want
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old) VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
explicit V3GraphVertex(V3Graph* graphp) VL_MT_DISABLED;
|
||||
//! Clone copy constructor. Doesn't copy edges or user/userp.
|
||||
virtual V3GraphVertex* clone(V3Graph* graphp) const VL_MT_DISABLED {
|
||||
return new V3GraphVertex{graphp, *this};
|
||||
}
|
||||
virtual ~V3GraphVertex() = default;
|
||||
void unlinkEdges(V3Graph* graphp) VL_MT_DISABLED;
|
||||
void unlinkDelete(V3Graph* graphp) VL_MT_DISABLED;
|
||||
|
||||
// METHODS
|
||||
// Return true iff of type T
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
static_assert(std::is_base_of<V3GraphVertex, T>::value,
|
||||
"'T' must be a subtype of V3GraphVertex");
|
||||
static_assert(std::is_same<typename std::remove_cv<T>::type,
|
||||
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
|
||||
"Missing VL_RTTI_IMPL(...) call in 'T'");
|
||||
return this->isInstanceOfClassWithId(T::rttiClassId());
|
||||
}
|
||||
|
||||
// Return cast to subtype T and assert of that type
|
||||
template <typename T>
|
||||
T* as() {
|
||||
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
template <typename T>
|
||||
const T* as() const {
|
||||
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
// Return cast to subtype T, else nullptr if different type
|
||||
template <typename T>
|
||||
T* cast() {
|
||||
return is<T>() ? static_cast<T*>(this) : nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
const T* cast() const {
|
||||
return is<T>() ? static_cast<const T*>(this) : nullptr;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const { return ""; }
|
||||
virtual string dotColor() const { return "black"; }
|
||||
virtual string dotShape() const { return ""; }
|
||||
virtual string dotStyle() const { return ""; }
|
||||
virtual string dotName() const { return ""; }
|
||||
virtual string dotRank() const { return ""; }
|
||||
virtual uint32_t rankAdder() const { return 1; }
|
||||
virtual FileLine* fileline() const { return nullptr; } // nullptr for unknown
|
||||
virtual int sortCmp(const V3GraphVertex* rhsp) const {
|
||||
// LHS goes first if of lower rank, or lower fanout
|
||||
if (m_rank < rhsp->m_rank) return -1;
|
||||
if (m_rank > rhsp->m_rank) return 1;
|
||||
if (m_fanout < rhsp->m_fanout) return -1;
|
||||
if (m_fanout > rhsp->m_fanout) return 1;
|
||||
return 0;
|
||||
}
|
||||
uint32_t color() const { return m_color; }
|
||||
void color(uint32_t color) { m_color = color; }
|
||||
uint32_t rank() const { return m_rank; }
|
||||
void rank(uint32_t rank) { m_rank = rank; }
|
||||
double fanout() const { return m_fanout; }
|
||||
void user(uint32_t user) { m_user = user; }
|
||||
uint32_t user() const VL_MT_STABLE { return m_user; }
|
||||
void userp(void* userp) { m_userp = userp; }
|
||||
void* userp() const VL_MT_STABLE { return m_userp; }
|
||||
// ITERATORS
|
||||
V3GraphVertex* verticesNextp() const { return m_vertices.nextp(); }
|
||||
V3GraphEdge* inBeginp() const { return m_ins.begin(); }
|
||||
bool inEmpty() const { return inBeginp() == nullptr; }
|
||||
bool inSize1() const VL_MT_DISABLED;
|
||||
V3GraphEdge* outBeginp() const { return m_outs.begin(); }
|
||||
bool outEmpty() const { return outBeginp() == nullptr; }
|
||||
bool outSize1() const VL_MT_DISABLED;
|
||||
V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); }
|
||||
// METHODS
|
||||
/// Error reporting
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED;
|
||||
void v3errorEndFatal(std::ostringstream& str) const
|
||||
VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED;
|
||||
/// Edges are routed around this vertex to point from "from" directly to "to"
|
||||
void rerouteEdges(V3Graph* graphp) VL_MT_DISABLED;
|
||||
/// Find the edge connecting ap and bp, where bp is wayward from ap.
|
||||
/// If edge is not found returns nullptr. O(edges) performance.
|
||||
V3GraphEdge* findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) VL_MT_DISABLED;
|
||||
|
||||
//============================================================================
|
||||
// TODO should we have a smaller edge structure when we don't need weight etc?
|
||||
|
||||
class V3GraphEdge VL_NOT_FINAL {
|
||||
VL_RTTI_IMPL_BASE(V3GraphEdge)
|
||||
// Wires/variables aren't edges. Edges have only a single to/from vertex
|
||||
public:
|
||||
// ENUMS
|
||||
enum Cutable : uint8_t { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge
|
||||
protected:
|
||||
friend class V3Graph;
|
||||
friend class V3GraphVertex;
|
||||
friend class GraphAcyc;
|
||||
friend class GraphAcycEdge;
|
||||
|
||||
V3ListEnt<V3GraphEdge*> m_outs; // Next outbound edge for same vertex (linked list)
|
||||
V3ListEnt<V3GraphEdge*> m_ins; // Next inbound edge for same vertex (linked list)
|
||||
//
|
||||
V3GraphVertex* m_fromp; // Vertices pointing to this edge
|
||||
V3GraphVertex* m_top; // Vertices this edge points to
|
||||
int m_weight; // Weight of the connection
|
||||
bool m_cutable; // Interconnect may be broken in order sorting
|
||||
union {
|
||||
void* m_userp; // Marker for some algorithms
|
||||
uint64_t m_user; // Marker for some algorithms
|
||||
};
|
||||
// METHODS
|
||||
void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
|
||||
bool cutable = false) VL_MT_DISABLED;
|
||||
void cut() { m_weight = 0; } // 0 weight is same as disconnected
|
||||
void outPushBack() VL_MT_DISABLED;
|
||||
void inPushBack() VL_MT_DISABLED;
|
||||
// CONSTRUCTORS
|
||||
protected:
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const V3GraphEdge& old) VL_MT_DISABLED {
|
||||
init(graphp, fromp, top, old.m_weight, old.m_cutable);
|
||||
}
|
||||
|
||||
public:
|
||||
//! Add DAG from one node to the specified node
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
|
||||
bool cutable = false) VL_MT_DISABLED {
|
||||
init(graphp, fromp, top, weight, cutable);
|
||||
}
|
||||
//! Clone copy constructor. Doesn't copy existing vertices or user/userp.
|
||||
virtual V3GraphEdge* clone(V3Graph* graphp, V3GraphVertex* fromp,
|
||||
V3GraphVertex* top) const VL_MT_DISABLED {
|
||||
return new V3GraphEdge{graphp, fromp, top, *this};
|
||||
}
|
||||
virtual ~V3GraphEdge() = default;
|
||||
// METHODS
|
||||
// Return true iff of type T
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
static_assert(std::is_base_of<V3GraphEdge, T>::value,
|
||||
"'T' must be a subtype of V3GraphEdge");
|
||||
static_assert(std::is_same<typename std::remove_cv<T>::type,
|
||||
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
|
||||
"Missing VL_RTTI_IMPL(...) call in 'T'");
|
||||
return this->isInstanceOfClassWithId(T::rttiClassId());
|
||||
}
|
||||
|
||||
// Return cast to subtype T and assert of that type
|
||||
template <typename T>
|
||||
T* as() {
|
||||
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
template <typename T>
|
||||
const T* as() const {
|
||||
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
// Return cast to subtype T, else nullptr if different type
|
||||
template <typename T>
|
||||
T* cast() {
|
||||
return is<T>() ? static_cast<T*>(this) : nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
const T* cast() const {
|
||||
return is<T>() ? static_cast<const T*>(this) : nullptr;
|
||||
}
|
||||
|
||||
virtual string name() const { return m_fromp->name() + "->" + m_top->name(); }
|
||||
virtual string dotLabel() const { return ""; }
|
||||
virtual string dotColor() const { return cutable() ? "yellowGreen" : "red"; }
|
||||
virtual string dotStyle() const { return cutable() ? "dashed" : ""; }
|
||||
virtual int sortCmp(const V3GraphEdge* rhsp) const {
|
||||
if (!m_weight || !rhsp->m_weight) return 0;
|
||||
return top()->sortCmp(rhsp->top());
|
||||
}
|
||||
void unlinkDelete() VL_MT_DISABLED;
|
||||
V3GraphEdge* relinkFromp(V3GraphVertex* newFromp) VL_MT_DISABLED;
|
||||
V3GraphEdge* relinkTop(V3GraphVertex* newTop) VL_MT_DISABLED;
|
||||
// ACCESSORS
|
||||
int weight() const { return m_weight; }
|
||||
void weight(int weight) { m_weight = weight; }
|
||||
bool cutable() const { return m_cutable; }
|
||||
void cutable(bool cutable) { m_cutable = cutable; }
|
||||
void userp(void* user) { m_userp = user; }
|
||||
void* userp() const { return m_userp; }
|
||||
void user(uint64_t user) { m_user = user; }
|
||||
uint64_t user() const { return m_user; }
|
||||
V3GraphVertex* fromp() const { return m_fromp; }
|
||||
V3GraphVertex* top() const { return m_top; }
|
||||
V3GraphVertex* closerp(GraphWay way) const { return way.forward() ? fromp() : top(); }
|
||||
V3GraphVertex* furtherp(GraphWay way) const { return way.forward() ? top() : fromp(); }
|
||||
// STATIC ACCESSORS
|
||||
static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; }
|
||||
static bool followAlwaysTrue(const V3GraphEdge*) { return true; }
|
||||
// ITERATORS
|
||||
V3GraphEdge* outNextp() const { return m_outs.nextp(); }
|
||||
V3GraphEdge* inNextp() const { return m_ins.nextp(); }
|
||||
V3GraphEdge* nextp(GraphWay way) const { return way.forward() ? outNextp() : inNextp(); }
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -39,11 +39,18 @@ class GraphAcycVertex final : public V3GraphVertex {
|
|||
V3GraphVertex* const m_origVertexp; // Pointer to first vertex this represents
|
||||
protected:
|
||||
friend class GraphAcyc;
|
||||
V3ListEnt<GraphAcycVertex*> m_work; // List of vertices with optimization work left
|
||||
V3ListLinks<GraphAcycVertex> m_links; // List links to store instances of this class
|
||||
uint32_t m_storedRank = 0; // Rank held until commit to edge placement
|
||||
bool m_onWorkList = false; // True if already on list of work to do
|
||||
bool m_deleted = false; // True if deleted
|
||||
|
||||
private:
|
||||
V3ListLinks<GraphAcycVertex>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List type to store instances of this class
|
||||
using List = V3List<GraphAcycVertex, &GraphAcycVertex::links>;
|
||||
|
||||
GraphAcycVertex(V3Graph* graphp, V3GraphVertex* origVertexp)
|
||||
: V3GraphVertex{graphp}
|
||||
, m_origVertexp{origVertexp} {}
|
||||
|
|
@ -103,7 +110,7 @@ class GraphAcyc final {
|
|||
// MEMBERS
|
||||
V3Graph* const m_origGraphp; // Original graph
|
||||
V3Graph m_breakGraph; // Graph with only breakable edges represented
|
||||
V3List<GraphAcycVertex*> m_work; // List of vertices with optimization work left
|
||||
GraphAcycVertex::List m_work; // List of vertices with optimization work left
|
||||
std::vector<OrigEdgeList*> m_origEdgeDelp; // List of deletions to do when done
|
||||
const V3EdgeFuncP
|
||||
m_origEdgeFuncp; // Function that says we follow this edge (in original graph)
|
||||
|
|
@ -127,12 +134,11 @@ class GraphAcyc final {
|
|||
bool origFollowEdge(V3GraphEdge* edgep) {
|
||||
return (edgep->weight() && (m_origEdgeFuncp)(edgep));
|
||||
}
|
||||
V3GraphEdge* edgeFromEdge(V3GraphEdge* oldedgep, V3GraphVertex* fromp, V3GraphVertex* top) {
|
||||
void edgeFromEdge(V3GraphEdge* oldedgep, V3GraphVertex* fromp, V3GraphVertex* top) {
|
||||
// Make new breakGraph edge, with old edge as a template
|
||||
GraphAcycEdge* const newEdgep = new GraphAcycEdge{&m_breakGraph, fromp, top,
|
||||
oldedgep->weight(), oldedgep->cutable()};
|
||||
newEdgep->userp(oldedgep->userp()); // Keep pointer to OrigEdgeList
|
||||
return newEdgep;
|
||||
}
|
||||
void addOrigEdgep(V3GraphEdge* toEdgep, V3GraphEdge* addEdgep) {
|
||||
// Add addEdge (or it's list) to list of edges that break edge represents
|
||||
|
|
@ -172,14 +178,14 @@ class GraphAcyc final {
|
|||
// Add vertex to list of nodes needing further optimization trials
|
||||
if (!avertexp->m_onWorkList) {
|
||||
avertexp->m_onWorkList = true;
|
||||
avertexp->m_work.pushBack(m_work, avertexp);
|
||||
m_work.linkBack(avertexp);
|
||||
}
|
||||
}
|
||||
GraphAcycVertex* workBeginp() { return m_work.begin(); }
|
||||
GraphAcycVertex* workBeginp() { return m_work.frontp(); }
|
||||
void workPop() {
|
||||
GraphAcycVertex* const avertexp = workBeginp();
|
||||
avertexp->m_onWorkList = false;
|
||||
avertexp->m_work.unlink(m_work, avertexp);
|
||||
m_work.unlink(avertexp);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -203,37 +209,35 @@ void GraphAcyc::buildGraph(V3Graph* origGraphp) {
|
|||
// For each old node, make a new graph node for optimization
|
||||
origGraphp->userClearVertices();
|
||||
origGraphp->userClearEdges();
|
||||
for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); overtexp;
|
||||
overtexp = overtexp->verticesNextp()) {
|
||||
if (overtexp->color()) {
|
||||
GraphAcycVertex* const avertexp = new GraphAcycVertex{&m_breakGraph, overtexp};
|
||||
overtexp->userp(avertexp); // Stash so can look up later
|
||||
for (V3GraphVertex& overtex : origGraphp->vertices()) {
|
||||
if (overtex.color()) {
|
||||
GraphAcycVertex* const avertexp = new GraphAcycVertex{&m_breakGraph, &overtex};
|
||||
overtex.userp(avertexp); // Stash so can look up later
|
||||
}
|
||||
}
|
||||
|
||||
// Build edges between logic vertices
|
||||
for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); overtexp;
|
||||
overtexp = overtexp->verticesNextp()) {
|
||||
if (overtexp->color()) {
|
||||
GraphAcycVertex* const avertexp = static_cast<GraphAcycVertex*>(overtexp->userp());
|
||||
buildGraphIterate(overtexp, avertexp);
|
||||
for (V3GraphVertex& overtex : origGraphp->vertices()) {
|
||||
if (overtex.color()) {
|
||||
GraphAcycVertex* const avertexp = static_cast<GraphAcycVertex*>(overtex.userp());
|
||||
buildGraphIterate(&overtex, avertexp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphAcyc::buildGraphIterate(V3GraphVertex* overtexp, GraphAcycVertex* avertexp) {
|
||||
// Make new edges
|
||||
for (V3GraphEdge* edgep = overtexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (origFollowEdge(edgep)) { // not cut
|
||||
const V3GraphVertex* toVertexp = edgep->top();
|
||||
for (V3GraphEdge& edge : overtexp->outEdges()) {
|
||||
if (origFollowEdge(&edge)) { // not cut
|
||||
const V3GraphVertex* toVertexp = edge.top();
|
||||
if (toVertexp->color()) {
|
||||
GraphAcycVertex* const toAVertexp
|
||||
= static_cast<GraphAcycVertex*>(toVertexp->userp());
|
||||
// Replicate the old edge into the new graph
|
||||
// There may be multiple edges between same pairs of vertices
|
||||
V3GraphEdge* breakEdgep = new GraphAcycEdge{&m_breakGraph, avertexp, toAVertexp,
|
||||
edgep->weight(), edgep->cutable()};
|
||||
addOrigEdgep(breakEdgep, edgep); // So can find original edge
|
||||
edge.weight(), edge.cutable()};
|
||||
addOrigEdgep(breakEdgep, &edge); // So can find original edge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -241,10 +245,7 @@ void GraphAcyc::buildGraphIterate(V3GraphVertex* overtexp, GraphAcycVertex* aver
|
|||
|
||||
void GraphAcyc::simplify(bool allowCut) {
|
||||
// Add all nodes to list of work to do
|
||||
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
workPush(vertexp);
|
||||
}
|
||||
for (V3GraphVertex& vertex : m_breakGraph.vertices()) workPush(&vertex);
|
||||
// Optimize till everything finished
|
||||
while (GraphAcycVertex* vertexp = workBeginp()) {
|
||||
workPop();
|
||||
|
|
@ -266,10 +267,8 @@ void GraphAcyc::simplify(bool allowCut) {
|
|||
|
||||
void GraphAcyc::deleteMarked() {
|
||||
// Delete nodes marked for removal
|
||||
for (V3GraphVertex *nextp, *vertexp = m_breakGraph.verticesBeginp(); vertexp;
|
||||
vertexp = nextp) {
|
||||
nextp = vertexp->verticesNextp();
|
||||
GraphAcycVertex* const avertexp = static_cast<GraphAcycVertex*>(vertexp);
|
||||
for (V3GraphVertex* const vtxp : m_breakGraph.vertices().unlinkable()) {
|
||||
GraphAcycVertex* const avertexp = static_cast<GraphAcycVertex*>(vtxp);
|
||||
if (avertexp->isDelete()) {
|
||||
VL_DO_DANGLING(avertexp->unlinkDelete(&m_breakGraph), avertexp);
|
||||
}
|
||||
|
|
@ -284,17 +283,13 @@ void GraphAcyc::simplifyNone(GraphAcycVertex* avertexp) {
|
|||
UINFO(9, " SimplifyNoneRemove " << avertexp << endl);
|
||||
avertexp->setDelete(); // Mark so we won't delete it twice
|
||||
// Remove edges
|
||||
while (V3GraphEdge* const edgep = avertexp->outBeginp()) {
|
||||
V3GraphVertex* otherVertexp = edgep->top();
|
||||
// UINFO(9, " out " << otherVertexp << endl);
|
||||
while (V3GraphEdge* const edgep = avertexp->outEdges().frontp()) {
|
||||
workPush(edgep->top());
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
workPush(otherVertexp);
|
||||
}
|
||||
while (V3GraphEdge* const edgep = avertexp->inBeginp()) {
|
||||
V3GraphVertex* otherVertexp = edgep->fromp();
|
||||
// UINFO(9, " in " << otherVertexp << endl);
|
||||
while (V3GraphEdge* const edgep = avertexp->inEdges().frontp()) {
|
||||
workPush(edgep->fromp());
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
workPush(otherVertexp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -303,10 +298,10 @@ void GraphAcyc::simplifyOne(GraphAcycVertex* avertexp) {
|
|||
// If a node has one input and one output, we can remove it and change the edges
|
||||
if (avertexp->isDelete()) return;
|
||||
if (avertexp->inSize1() && avertexp->outSize1()) {
|
||||
V3GraphEdge* inEdgep = avertexp->inBeginp();
|
||||
V3GraphEdge* outEdgep = avertexp->outBeginp();
|
||||
V3GraphVertex* inVertexp = inEdgep->fromp();
|
||||
V3GraphVertex* outVertexp = outEdgep->top();
|
||||
V3GraphEdge* const inEdgep = avertexp->inEdges().frontp();
|
||||
V3GraphEdge* const outEdgep = avertexp->outEdges().frontp();
|
||||
V3GraphVertex* const inVertexp = inEdgep->fromp();
|
||||
V3GraphVertex* const outVertexp = outEdgep->top();
|
||||
// The in and out may be the same node; we'll make a loop
|
||||
// The in OR out may be THIS node; we can't delete it then.
|
||||
if (inVertexp != avertexp && outVertexp != avertexp) {
|
||||
|
|
@ -317,7 +312,7 @@ void GraphAcyc::simplifyOne(GraphAcycVertex* avertexp) {
|
|||
// We can forget about the origEdge list for the "non-selected" set of edges,
|
||||
// as we need to break only one set or the other set of edges, not both.
|
||||
// (This is why we must give preference to the cutable set.)
|
||||
V3GraphEdge* templateEdgep
|
||||
V3GraphEdge* const templateEdgep
|
||||
= ((inEdgep->cutable()
|
||||
&& (!outEdgep->cutable() || inEdgep->weight() < outEdgep->weight()))
|
||||
? inEdgep
|
||||
|
|
@ -339,13 +334,12 @@ void GraphAcyc::simplifyOut(GraphAcycVertex* avertexp) {
|
|||
// to the next node in the list
|
||||
if (avertexp->isDelete()) return;
|
||||
if (avertexp->outSize1()) {
|
||||
V3GraphEdge* outEdgep = avertexp->outBeginp();
|
||||
V3GraphEdge* const outEdgep = avertexp->outEdges().frontp();
|
||||
if (!outEdgep->cutable()) {
|
||||
V3GraphVertex* outVertexp = outEdgep->top();
|
||||
UINFO(9, " SimplifyOutRemove " << avertexp << endl);
|
||||
avertexp->setDelete(); // Mark so we won't delete it twice
|
||||
for (V3GraphEdge *nextp, *inEdgep = avertexp->inBeginp(); inEdgep; inEdgep = nextp) {
|
||||
nextp = inEdgep->inNextp();
|
||||
for (V3GraphEdge* const inEdgep : avertexp->inEdges().unlinkable()) {
|
||||
V3GraphVertex* inVertexp = inEdgep->fromp();
|
||||
if (inVertexp == avertexp) {
|
||||
if (debug()) v3error("Non-cutable vertex=" << avertexp); // LCOV_EXCL_LINE
|
||||
|
|
@ -375,12 +369,9 @@ void GraphAcyc::simplifyDup(GraphAcycVertex* avertexp) {
|
|||
// Remove redundant edges
|
||||
if (avertexp->isDelete()) return;
|
||||
// Clear marks
|
||||
for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
edgep->top()->userp(nullptr);
|
||||
}
|
||||
for (V3GraphEdge& edge : avertexp->outEdges()) edge.top()->userp(nullptr);
|
||||
// Mark edges and detect duplications
|
||||
for (V3GraphEdge *nextp, *edgep = avertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge* const edgep : avertexp->outEdges().unlinkable()) {
|
||||
V3GraphVertex* outVertexp = edgep->top();
|
||||
V3GraphEdge* prevEdgep = static_cast<V3GraphEdge*>(outVertexp->userp());
|
||||
if (prevEdgep) {
|
||||
|
|
@ -413,8 +404,7 @@ void GraphAcyc::simplifyDup(GraphAcycVertex* avertexp) {
|
|||
void GraphAcyc::cutBasic(GraphAcycVertex* avertexp) {
|
||||
// Detect and cleanup any loops from node to itself
|
||||
if (avertexp->isDelete()) return;
|
||||
for (V3GraphEdge *nextp, *edgep = avertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge* const edgep : avertexp->outEdges().unlinkable()) {
|
||||
if (edgep->cutable() && edgep->top() == avertexp) {
|
||||
cutOrigEdge(edgep, " Cut Basic");
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
|
|
@ -427,15 +417,12 @@ void GraphAcyc::cutBackward(GraphAcycVertex* avertexp) {
|
|||
// If a cutable edge is from A->B, and there's a non-cutable edge B->A, then must cut!
|
||||
if (avertexp->isDelete()) return;
|
||||
// Clear marks
|
||||
for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
edgep->top()->user(false);
|
||||
}
|
||||
for (V3GraphEdge* edgep = avertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
if (!edgep->cutable()) edgep->fromp()->user(true);
|
||||
for (V3GraphEdge& edge : avertexp->outEdges()) edge.top()->user(false);
|
||||
for (V3GraphEdge& edge : avertexp->inEdges()) {
|
||||
if (!edge.cutable()) edge.fromp()->user(true);
|
||||
}
|
||||
// Detect duplications
|
||||
for (V3GraphEdge *nextp, *edgep = avertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge* const edgep : avertexp->outEdges().unlinkable()) {
|
||||
if (edgep->cutable() && edgep->top()->user()) {
|
||||
cutOrigEdge(edgep, " Cut A->B->A");
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
|
|
@ -449,10 +436,9 @@ void GraphAcyc::place() {
|
|||
|
||||
// Make a list of all cutable edges in the graph
|
||||
int numEdges = 0;
|
||||
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() && edgep->cutable()) ++numEdges;
|
||||
for (V3GraphVertex& vertex : m_breakGraph.vertices()) {
|
||||
for (V3GraphEdge& edge : vertex.outEdges()) {
|
||||
if (edge.weight() && edge.cutable()) ++numEdges;
|
||||
}
|
||||
}
|
||||
UINFO(4, " Cutable edges = " << numEdges << endl);
|
||||
|
|
@ -460,11 +446,10 @@ void GraphAcyc::place() {
|
|||
std::vector<V3GraphEdge*> edges; // List of all edges to be processed
|
||||
// Make the vector properly sized right off the bat -- faster than reallocating
|
||||
edges.reserve(numEdges + 1);
|
||||
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
vertexp->user(0); // Clear in prep of next step
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() && edgep->cutable()) edges.push_back(edgep);
|
||||
for (V3GraphVertex& vertex : m_breakGraph.vertices()) {
|
||||
vertex.user(0); // Clear in prep of next step
|
||||
for (V3GraphEdge& edge : vertex.outEdges()) {
|
||||
if (edge.weight() && edge.cutable()) edges.push_back(&edge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,9 +507,9 @@ bool GraphAcyc::placeIterate(GraphAcycVertex* vertexp, uint32_t currentRank) {
|
|||
}
|
||||
vertexp->rank(currentRank);
|
||||
// Follow all edges and increase their ranks
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() && !edgep->cutable()) {
|
||||
if (placeIterate(static_cast<GraphAcycVertex*>(edgep->top()), currentRank + 1)) {
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (edge.weight() && !edge.cutable()) {
|
||||
if (placeIterate(static_cast<GraphAcycVertex*>(edge.top()), currentRank + 1)) {
|
||||
// We don't need to reset user(); we'll use a different placeStep for the next edge
|
||||
return true; // Loop detected
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,19 +42,15 @@ class GraphRemoveRedundant final : GraphAlg<> {
|
|||
const bool m_sumWeights; ///< Sum, rather then maximize weights
|
||||
private:
|
||||
void main() {
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
vertexIterate(vertexp);
|
||||
}
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) vertexIterate(&vertex);
|
||||
}
|
||||
void vertexIterate(V3GraphVertex* vertexp) {
|
||||
// Clear marks
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
edgep->top()->userp(nullptr);
|
||||
}
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) edge.top()->userp(nullptr);
|
||||
|
||||
// Mark edges and detect duplications
|
||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
|
||||
for (V3GraphEdge* const edgep : vertexp->outEdges().unlinkable()) {
|
||||
if (followEdge(edgep)) {
|
||||
V3GraphVertex* outVertexp = edgep->top();
|
||||
V3GraphEdge* prevEdgep = static_cast<V3GraphEdge*>(outVertexp->userp());
|
||||
|
|
@ -112,15 +108,15 @@ public:
|
|||
: GraphAlg<>(graphp, nullptr) {}
|
||||
void go() {
|
||||
GraphPathChecker checker{m_graphp};
|
||||
for (V3GraphVertex* vxp = m_graphp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
for (V3GraphVertex& vtx : m_graphp->vertices()) {
|
||||
V3GraphEdge* deletep = nullptr;
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
for (V3GraphEdge& edge : vtx.outEdges()) {
|
||||
if (deletep) VL_DO_CLEAR(deletep->unlinkDelete(), deletep = nullptr);
|
||||
// It should be safe to modify the graph, despite using
|
||||
// the GraphPathChecker, as none of the modifications will
|
||||
// change what can be reached from what, nor should they
|
||||
// change the rank or CP of any node.
|
||||
if (checker.isTransitiveEdge(edgep)) deletep = edgep;
|
||||
if (checker.isTransitiveEdge(&edge)) deletep = &edge;
|
||||
}
|
||||
if (deletep) VL_DO_DANGLING(deletep->unlinkDelete(), deletep);
|
||||
}
|
||||
|
|
@ -143,10 +139,9 @@ class GraphAlgWeakly final : GraphAlg<> {
|
|||
m_graphp->clearColors();
|
||||
// Color graph
|
||||
uint32_t currentColor = 0;
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
currentColor++;
|
||||
vertexIterate(vertexp, currentColor);
|
||||
vertexIterate(&vertex, currentColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,11 +150,11 @@ class GraphAlgWeakly final : GraphAlg<> {
|
|||
// then visit each of its edges, giving them the same color
|
||||
if (vertexp->color()) return; // Already colored it
|
||||
vertexp->color(currentColor);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (followEdge(edgep)) vertexIterate(edgep->top(), currentColor);
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (followEdge(&edge)) vertexIterate(edge.top(), currentColor);
|
||||
}
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
if (followEdge(edgep)) vertexIterate(edgep->fromp(), currentColor);
|
||||
for (V3GraphEdge& edge : vertexp->inEdges()) {
|
||||
if (followEdge(&edge)) vertexIterate(edge.fromp(), currentColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,33 +187,30 @@ class GraphAlgStrongly final : GraphAlg<> {
|
|||
// Vertex::color // Output subtree number (fully processed)
|
||||
|
||||
// Clear info
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
vertexp->color(0);
|
||||
vertexp->user(0);
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
vertex.color(0);
|
||||
vertex.user(0);
|
||||
}
|
||||
// Color graph
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
if (!vertexp->user()) {
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
if (!vertex.user()) {
|
||||
m_currentDfs++;
|
||||
vertexIterate(vertexp);
|
||||
vertexIterate(&vertex);
|
||||
}
|
||||
}
|
||||
// If there's a single vertex of a color, it doesn't need a subgraph
|
||||
// This simplifies the consumer's code, and reduces graph debugging clutter
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
bool onecolor = true;
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (followEdge(edgep)) {
|
||||
if (vertexp->color() == edgep->top()->color()) {
|
||||
for (V3GraphEdge& edge : vertex.outEdges()) {
|
||||
if (followEdge(&edge)) {
|
||||
if (vertex.color() == edge.top()->color()) {
|
||||
onecolor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (onecolor) vertexp->color(0);
|
||||
if (onecolor) vertex.color(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,9 +218,9 @@ class GraphAlgStrongly final : GraphAlg<> {
|
|||
const uint32_t thisDfsNum = m_currentDfs++;
|
||||
vertexp->user(thisDfsNum);
|
||||
vertexp->color(0);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (followEdge(edgep)) {
|
||||
V3GraphVertex* top = edgep->top();
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (followEdge(&edge)) {
|
||||
V3GraphVertex* top = edge.top();
|
||||
if (!top->user()) { // Dest not computed yet
|
||||
vertexIterate(top);
|
||||
}
|
||||
|
|
@ -273,15 +265,13 @@ class GraphAlgRank final : GraphAlg<> {
|
|||
// Rank each vertex, ignoring cutable edges
|
||||
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
|
||||
// Clear existing ranks
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
vertexp->rank(0);
|
||||
vertexp->user(0);
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
vertex.rank(0);
|
||||
vertex.user(0);
|
||||
}
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
if (!vertexp->user()) { //
|
||||
vertexIterate(vertexp, 1);
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
if (!vertex.user()) { //
|
||||
vertexIterate(&vertex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -298,10 +288,8 @@ class GraphAlgRank final : GraphAlg<> {
|
|||
if (vertexp->rank() >= currentRank) return; // Already processed it
|
||||
vertexp->user(1);
|
||||
vertexp->rank(currentRank);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (followEdge(edgep)) {
|
||||
vertexIterate(edgep->top(), currentRank + vertexp->rankAdder());
|
||||
}
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (followEdge(&edge)) vertexIterate(edge.top(), currentRank + vertexp->rankAdder());
|
||||
}
|
||||
vertexp->user(2);
|
||||
}
|
||||
|
|
@ -353,8 +341,8 @@ class GraphAlgRLoops final : GraphAlg<> {
|
|||
}
|
||||
if (vertexp->user() == 2) return; // Already processed it
|
||||
vertexp->user(1);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (followEdge(edgep)) vertexIterate(edgep->top(), currentRank);
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (followEdge(&edge)) vertexIterate(edge.top(), currentRank);
|
||||
}
|
||||
vertexp->user(2);
|
||||
}
|
||||
|
|
@ -387,13 +375,13 @@ class GraphAlgSubtrees final : GraphAlg<> {
|
|||
newVertexp = vertexp->clone(m_loopGraphp);
|
||||
vertexp->userp(newVertexp);
|
||||
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (followEdge(edgep)) {
|
||||
V3GraphEdge* newEdgep = static_cast<V3GraphEdge*>(edgep->userp());
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (followEdge(&edge)) {
|
||||
V3GraphEdge* newEdgep = static_cast<V3GraphEdge*>(edge.userp());
|
||||
if (!newEdgep) {
|
||||
V3GraphVertex* newTop = vertexIterateAll(edgep->top());
|
||||
newEdgep = edgep->clone(m_loopGraphp, newVertexp, newTop);
|
||||
edgep->userp(newEdgep);
|
||||
V3GraphVertex* newTop = vertexIterateAll(edge.top());
|
||||
newEdgep = edge.clone(m_loopGraphp, newVertexp, newTop);
|
||||
edge.userp(newEdgep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -438,30 +426,25 @@ struct GraphSortEdgeCmp final {
|
|||
void V3Graph::sortVertices() {
|
||||
// Sort list of vertices by rank, then fanout
|
||||
std::vector<V3GraphVertex*> vertices;
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
vertices.push_back(vertexp);
|
||||
}
|
||||
for (V3GraphVertex& vertex : m_vertices) vertices.push_back(&vertex);
|
||||
std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp());
|
||||
this->verticesUnlink();
|
||||
for (V3GraphVertex* ip : vertices) ip->verticesPushBack(this);
|
||||
// Re-insert in sorted order
|
||||
for (V3GraphVertex* const ip : vertices) {
|
||||
m_vertices.unlink(ip);
|
||||
m_vertices.linkBack(ip);
|
||||
}
|
||||
}
|
||||
|
||||
void V3Graph::sortEdges() {
|
||||
// Sort edges by rank then fanout of node they point to
|
||||
std::vector<V3GraphEdge*> edges;
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
for (V3GraphVertex& vertex : vertices()) {
|
||||
// Make a vector
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
edges.push_back(edgep);
|
||||
}
|
||||
for (V3GraphEdge& edge : vertex.outEdges()) edges.push_back(&edge);
|
||||
// Sort
|
||||
std::stable_sort(edges.begin(), edges.end(), GraphSortEdgeCmp());
|
||||
|
||||
// Relink edges in specified order
|
||||
// We know the vector contains all of the edges that were
|
||||
// there originally (didn't delete or add)
|
||||
vertexp->outUnlink();
|
||||
for (V3GraphEdge* edgep : edges) edgep->outPushBack();
|
||||
for (V3GraphEdge* const edgep : edges) edgep->relinkFromp(&vertex);
|
||||
// Prep for next
|
||||
edges.clear();
|
||||
}
|
||||
|
|
@ -487,8 +470,8 @@ void V3Graph::orderPreRanked() {
|
|||
// Compute fanouts
|
||||
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
|
||||
userClearVertices();
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
|
||||
if (!vertexp->user()) orderDFSIterate(vertexp);
|
||||
for (V3GraphVertex& vertex : vertices()) {
|
||||
if (!vertex.user()) orderDFSIterate(&vertex);
|
||||
}
|
||||
|
||||
// Sort list of vertices by rank, then fanout. Fanout is a bit of a
|
||||
|
|
@ -506,12 +489,12 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) {
|
|||
UASSERT_OBJ(vertexp->user() != 1, vertexp, "Loop found, backward edges should be dead");
|
||||
vertexp->user(1);
|
||||
double fanout = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight()) fanout += orderDFSIterate(edgep->m_top);
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (edge.weight()) fanout += orderDFSIterate(edge.top());
|
||||
}
|
||||
// Just count inbound edges
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
if (edgep->weight()) ++fanout;
|
||||
for (V3GraphEdge& edge : vertexp->inEdges()) {
|
||||
if (edge.weight()) ++fanout;
|
||||
}
|
||||
vertexp->fanout(fanout);
|
||||
vertexp->user(2);
|
||||
|
|
@ -524,12 +507,12 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) {
|
|||
|
||||
class GraphAlgParallelismReport final {
|
||||
// MEMBERS
|
||||
const V3Graph& m_graph; // The graph
|
||||
V3Graph& m_graph; // The graph
|
||||
const std::function<uint64_t(const V3GraphVertex*)> m_vertexCost; // vertex cost function
|
||||
V3Graph::ParallelismReport m_report; // The result report
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit GraphAlgParallelismReport(const V3Graph& graph,
|
||||
explicit GraphAlgParallelismReport(V3Graph& graph,
|
||||
std::function<uint64_t(const V3GraphVertex*)> vertexCost)
|
||||
: m_graph{graph}
|
||||
, m_vertexCost{vertexCost} {
|
||||
|
|
@ -540,13 +523,13 @@ class GraphAlgParallelismReport final {
|
|||
for (const V3GraphVertex* vertexp; (vertexp = serialize.nextp());) {
|
||||
++m_report.m_vertexCount;
|
||||
uint64_t cpCostToHere = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
for (const V3GraphEdge& edge : vertexp->inEdges()) {
|
||||
++m_report.m_edgeCount;
|
||||
// For each upstream item, add its critical path cost to
|
||||
// the cost of this edge, to form a new candidate critical
|
||||
// path cost to the current node. Whichever is largest is
|
||||
// the critical path to reach the start of this node.
|
||||
cpCostToHere = std::max(cpCostToHere, critPaths[edgep->fromp()]);
|
||||
cpCostToHere = std::max(cpCostToHere, critPaths[edge.fromp()]);
|
||||
}
|
||||
// Include the cost of the current vertex in the critical
|
||||
// path, so it represents the critical path to the end of
|
||||
|
|
@ -564,12 +547,12 @@ class GraphAlgParallelismReport final {
|
|||
|
||||
public:
|
||||
static V3Graph::ParallelismReport
|
||||
apply(const V3Graph& graph, std::function<uint32_t(const V3GraphVertex*)> vertexCost) {
|
||||
apply(V3Graph& graph, std::function<uint32_t(const V3GraphVertex*)> vertexCost) {
|
||||
return GraphAlgParallelismReport(graph, vertexCost).m_report;
|
||||
}
|
||||
};
|
||||
|
||||
V3Graph::ParallelismReport
|
||||
V3Graph::parallelismReport(std::function<uint64_t(const V3GraphVertex*)> vertexCost) const {
|
||||
V3Graph::parallelismReport(std::function<uint64_t(const V3GraphVertex*)> vertexCost) {
|
||||
return GraphAlgParallelismReport::apply(*this, vertexCost);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,36 +53,17 @@ struct GraphPCNode final {
|
|||
//######################################################################
|
||||
// GraphPathChecker implementation
|
||||
|
||||
GraphPathChecker::GraphPathChecker(const V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg<const V3Graph>{graphp, edgeFuncp} {
|
||||
for (V3GraphVertex* vxp = graphp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
// Setup tracking structure for each node. If delete a vertex
|
||||
// there would be a leak, but ok as accept only const V3Graph*'s.
|
||||
vxp->userp(new GraphPCNode);
|
||||
}
|
||||
// Init critical paths in userp() for each vertex
|
||||
initHalfCriticalPaths(GraphWay::FORWARD, false);
|
||||
initHalfCriticalPaths(GraphWay::REVERSE, false);
|
||||
}
|
||||
|
||||
GraphPathChecker::~GraphPathChecker() {
|
||||
// Free every GraphPCNode
|
||||
for (V3GraphVertex* vxp = m_graphp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
const GraphPCNode* const nodep = static_cast<GraphPCNode*>(vxp->userp());
|
||||
VL_DO_DANGLING(delete nodep, nodep);
|
||||
vxp->userp(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) {
|
||||
template <GraphWay::en T_Way>
|
||||
void GraphPathChecker::initHalfCriticalPaths(bool checkOnly) {
|
||||
constexpr GraphWay way{T_Way};
|
||||
constexpr GraphWay rev = way.invert();
|
||||
GraphStreamUnordered order(m_graphp, way);
|
||||
const GraphWay rev = way.invert();
|
||||
while (const V3GraphVertex* const vertexp = order.nextp()) {
|
||||
unsigned critPathCost = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->beginp(rev); edgep; edgep = edgep->nextp(rev)) {
|
||||
if (!m_edgeFuncp(edgep)) continue;
|
||||
for (const V3GraphEdge& edge : vertexp->edges<rev>()) {
|
||||
if (!m_edgeFuncp(&edge)) continue;
|
||||
|
||||
const V3GraphVertex* wrelativep = edgep->furtherp(rev);
|
||||
const V3GraphVertex* wrelativep = edge.furtherp<rev>();
|
||||
const GraphPCNode* const wrelUserp = static_cast<GraphPCNode*>(wrelativep->userp());
|
||||
critPathCost = std::max(critPathCost, wrelUserp->m_cp[way] + 1);
|
||||
}
|
||||
|
|
@ -97,6 +78,30 @@ void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
template void GraphPathChecker::initHalfCriticalPaths<GraphWay::FORWARD>(bool);
|
||||
template void GraphPathChecker::initHalfCriticalPaths<GraphWay::REVERSE>(bool);
|
||||
|
||||
GraphPathChecker::GraphPathChecker(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg<V3Graph>{graphp, edgeFuncp} {
|
||||
for (V3GraphVertex& vtx : graphp->vertices()) {
|
||||
// Setup tracking structure for each node. If delete a vertex
|
||||
// there would be a leak, but ok as accept only const V3Graph*'s.
|
||||
vtx.userp(new GraphPCNode);
|
||||
}
|
||||
// Init critical paths in userp() for each vertex
|
||||
initHalfCriticalPaths<GraphWay::FORWARD>(false);
|
||||
initHalfCriticalPaths<GraphWay::REVERSE>(false);
|
||||
}
|
||||
|
||||
GraphPathChecker::~GraphPathChecker() {
|
||||
// Free every GraphPCNode
|
||||
for (V3GraphVertex& vtx : m_graphp->vertices()) {
|
||||
const GraphPCNode* const nodep = static_cast<GraphPCNode*>(vtx.userp());
|
||||
VL_DO_DANGLING(delete nodep, nodep);
|
||||
vtx.userp(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphPathChecker::pathExistsInternal(const V3GraphVertex* ap, const V3GraphVertex* bp,
|
||||
unsigned* costp) {
|
||||
GraphPCNode* const auserp = static_cast<GraphPCNode*>(ap->userp());
|
||||
|
|
@ -121,11 +126,11 @@ bool GraphPathChecker::pathExistsInternal(const V3GraphVertex* ap, const V3Graph
|
|||
|
||||
// Slow path; visit some extended family
|
||||
bool foundPath = false;
|
||||
for (V3GraphEdge* edgep = ap->outBeginp(); edgep && !foundPath; edgep = edgep->outNextp()) {
|
||||
if (!m_edgeFuncp(edgep)) continue;
|
||||
for (const V3GraphEdge& edge : ap->outEdges()) {
|
||||
if (!m_edgeFuncp(&edge)) continue;
|
||||
|
||||
unsigned childCost;
|
||||
if (pathExistsInternal(edgep->top(), bp, &childCost)) foundPath = true;
|
||||
if (pathExistsInternal(edge.top(), bp, &childCost)) foundPath = true;
|
||||
if (costp) *costp += childCost;
|
||||
}
|
||||
|
||||
|
|
@ -141,10 +146,9 @@ bool GraphPathChecker::isTransitiveEdge(const V3GraphEdge* edgep) {
|
|||
const V3GraphVertex* fromp = edgep->fromp();
|
||||
const V3GraphVertex* top = edgep->top();
|
||||
incGeneration();
|
||||
for (const V3GraphEdge* fromOutp = fromp->outBeginp(); fromOutp;
|
||||
fromOutp = fromOutp->outNextp()) {
|
||||
if (fromOutp == edgep) continue;
|
||||
if (pathExistsInternal(fromOutp->top(), top)) return true;
|
||||
for (const V3GraphEdge& fromOut : fromp->outEdges()) {
|
||||
if (&fromOut == edgep) continue;
|
||||
if (pathExistsInternal(fromOut.top(), top)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
///
|
||||
/// The graph (or at least, the subset the algorithm sees through
|
||||
/// edgeFuncp) must not change during the lifetime of the checker.
|
||||
class GraphPathChecker final : GraphAlg<const V3Graph> {
|
||||
class GraphPathChecker final : GraphAlg<V3Graph> {
|
||||
// Count "generations" which increases on operations that scan through
|
||||
// the graph. Each node is marked with the last generation that scanned
|
||||
// it, to enable asserting there are no cycles, and to avoid recursing
|
||||
|
|
@ -39,9 +39,8 @@ class GraphPathChecker final : GraphAlg<const V3Graph> {
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit GraphPathChecker(const V3Graph* graphp,
|
||||
V3EdgeFuncP edgeFuncp
|
||||
= V3GraphEdge::followAlwaysTrue) VL_MT_DISABLED;
|
||||
explicit GraphPathChecker(V3Graph* graphp, V3EdgeFuncP edgeFuncp
|
||||
= V3GraphEdge::followAlwaysTrue) VL_MT_DISABLED;
|
||||
~GraphPathChecker() VL_MT_DISABLED;
|
||||
|
||||
// METHODS
|
||||
|
|
@ -55,7 +54,8 @@ public:
|
|||
private:
|
||||
bool pathExistsInternal(const V3GraphVertex* ap, const V3GraphVertex* bp,
|
||||
unsigned* costp = nullptr) VL_MT_DISABLED;
|
||||
void initHalfCriticalPaths(GraphWay way, bool checkOnly) VL_MT_DISABLED;
|
||||
template <GraphWay::en T_Way>
|
||||
void initHalfCriticalPaths(bool checkOnly) VL_MT_DISABLED;
|
||||
void incGeneration() { ++m_generation; }
|
||||
|
||||
VL_UNCOPYABLE(GraphPathChecker);
|
||||
|
|
|
|||
|
|
@ -104,32 +104,25 @@ public:
|
|||
, m_last{m_readyVertices.end()}
|
||||
, m_way{way} {
|
||||
uint32_t pos = 0;
|
||||
for (const V3GraphVertex* vxp = graphp->verticesBeginp(); vxp;
|
||||
vxp = vxp->verticesNextp()) {
|
||||
for (const V3GraphVertex& vtx : graphp->vertices()) {
|
||||
// Every vertex initially is waiting, or ready.
|
||||
if (way == GraphWay::FORWARD) {
|
||||
if (vxp->inEmpty()) {
|
||||
const VxHolder newVx{vxp, pos++, 0};
|
||||
if (vtx.inEmpty()) {
|
||||
const VxHolder newVx{&vtx, pos++, 0};
|
||||
m_readyVertices.insert(newVx);
|
||||
} else {
|
||||
uint32_t depCount = 0;
|
||||
for (V3GraphEdge* depp = vxp->inBeginp(); depp; depp = depp->inNextp()) {
|
||||
++depCount;
|
||||
}
|
||||
const VxHolder newVx{vxp, pos++, depCount};
|
||||
m_waitingVertices.emplace(vxp, newVx);
|
||||
const uint32_t depCount = vtx.inEdges().size();
|
||||
const VxHolder newVx{&vtx, pos++, depCount};
|
||||
m_waitingVertices.emplace(&vtx, newVx);
|
||||
}
|
||||
} else { // REVERSE
|
||||
if (vxp->outEmpty()) {
|
||||
const VxHolder newVx{vxp, pos++, 0};
|
||||
if (vtx.outEmpty()) {
|
||||
const VxHolder newVx{&vtx, pos++, 0};
|
||||
m_readyVertices.insert(newVx);
|
||||
} else {
|
||||
uint32_t depCount = 0;
|
||||
for (V3GraphEdge* depp = vxp->outBeginp(); depp; depp = depp->outNextp()) {
|
||||
++depCount;
|
||||
}
|
||||
const VxHolder newVx{vxp, pos++, depCount};
|
||||
m_waitingVertices.emplace(vxp, newVx);
|
||||
const uint32_t depCount = vtx.outEdges().size();
|
||||
const VxHolder newVx{&vtx, pos++, depCount};
|
||||
m_waitingVertices.emplace(&vtx, newVx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -196,8 +189,8 @@ public:
|
|||
private:
|
||||
void unblockDeps(const V3GraphVertex* vertexp) {
|
||||
if (m_way == GraphWay::FORWARD) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const V3GraphVertex* const toVertexp = edgep->top();
|
||||
for (const V3GraphEdge& edgep : vertexp->outEdges()) {
|
||||
const V3GraphVertex* const toVertexp = edgep.top();
|
||||
|
||||
const auto it = m_waitingVertices.find(toVertexp);
|
||||
UASSERT_OBJ(it != m_waitingVertices.end(), toVertexp,
|
||||
|
|
@ -208,8 +201,8 @@ private:
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
const V3GraphVertex* const fromVertexp = edgep->fromp();
|
||||
for (const V3GraphEdge& edge : vertexp->inEdges()) {
|
||||
const V3GraphVertex* const fromVertexp = edge.fromp();
|
||||
|
||||
const auto it = m_waitingVertices.find(fromVertexp);
|
||||
UASSERT_OBJ(it != m_waitingVertices.end(), fromVertexp,
|
||||
|
|
@ -239,7 +232,7 @@ class GraphStreamUnordered final {
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(GraphStreamUnordered);
|
||||
explicit GraphStreamUnordered(const V3Graph* graphp, GraphWay way = GraphWay::FORWARD)
|
||||
explicit GraphStreamUnordered(V3Graph* graphp, GraphWay way = GraphWay::FORWARD)
|
||||
: m_way{way} {
|
||||
if (m_way == GraphWay::FORWARD) {
|
||||
init<GraphWay::FORWARD>(graphp);
|
||||
|
|
@ -272,27 +265,21 @@ public:
|
|||
|
||||
private:
|
||||
template <uint8_t T_Way> //
|
||||
VL_ATTR_NOINLINE void init(const V3Graph* graphp) {
|
||||
VL_ATTR_NOINLINE void init(V3Graph* graphp) {
|
||||
constexpr GraphWay way{T_Way};
|
||||
constexpr GraphWay inv = way.invert();
|
||||
// Assign every vertex without an incoming edge to ready, others to waiting
|
||||
for (V3GraphVertex *vertexp = graphp->verticesBeginp(), *nextp; vertexp; vertexp = nextp) {
|
||||
nextp = vertexp->verticesNextp();
|
||||
uint32_t nDeps = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->beginp(inv); edgep; edgep = edgep->nextp(inv)) {
|
||||
++nDeps;
|
||||
}
|
||||
vertexp->color(nDeps); // Using color instead of user, as user might be used by client
|
||||
if (VL_UNLIKELY(nDeps == 0)) m_nextVertices.push_back(vertexp);
|
||||
for (V3GraphVertex& vertex : graphp->vertices()) {
|
||||
const uint32_t nDeps = vertex.edges<way.invert()>().size();
|
||||
vertex.color(nDeps); // Using color instead of user, as user might be used by client
|
||||
if (VL_UNLIKELY(nDeps == 0)) m_nextVertices.push_back(&vertex);
|
||||
}
|
||||
}
|
||||
|
||||
template <uint8_t T_Way> //
|
||||
VL_ATTR_NOINLINE const V3GraphVertex* unblock(const V3GraphVertex* resultp) {
|
||||
constexpr GraphWay way{T_Way};
|
||||
for (V3GraphEdge *edgep = resultp->beginp(way), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->nextp(way);
|
||||
V3GraphVertex* const vertexp = edgep->furtherp(way);
|
||||
for (const V3GraphEdge& edge : resultp->edges<way>()) {
|
||||
V3GraphVertex* const vertexp = edge.furtherp<way>();
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(vertexp->color() != 0, vertexp, "Should not be on waiting list");
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ public:
|
|||
gp->order();
|
||||
|
||||
// Test debug function
|
||||
gp->dumpEdges(std::cout, ap);
|
||||
gp->dumpEdges(std::cout, *ap);
|
||||
|
||||
dumpSelf();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ class LifePostDlyVisitor final : public VNVisitor {
|
|||
// Map each dly var to its AstAssignPost* node and the location thereof
|
||||
std::unordered_map<const AstVarScope*, LifePostLocation> m_assignposts;
|
||||
|
||||
const V3Graph* m_mtasksGraphp = nullptr; // Mtask tracking graph
|
||||
V3Graph* m_mtasksGraphp = nullptr; // Mtask tracking graph
|
||||
std::unique_ptr<GraphPathChecker> m_checker;
|
||||
|
||||
const AstCFunc* const m_evalNbap; // The _eval__nba function
|
||||
|
|
@ -325,9 +325,8 @@ class LifePostDlyVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(!m_mtasksGraphp, nodep, "Cannot handle more than one AstExecGraph");
|
||||
m_mtasksGraphp = nodep->depGraphp();
|
||||
}
|
||||
for (V3GraphVertex* mtaskVxp = nodep->depGraphp()->verticesBeginp(); mtaskVxp;
|
||||
mtaskVxp = mtaskVxp->verticesNextp()) {
|
||||
const ExecMTask* const mtaskp = mtaskVxp->as<ExecMTask>();
|
||||
for (V3GraphVertex& mtaskVtx : nodep->depGraphp()->vertices()) {
|
||||
const ExecMTask* const mtaskp = mtaskVtx.as<ExecMTask>();
|
||||
m_execMTaskp = mtaskp;
|
||||
m_sequence = 0;
|
||||
iterate(mtaskp->bodyp());
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ class LinkCellsVisitor final : public VNVisitor {
|
|||
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
||||
if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells");
|
||||
m_graph.rank();
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (const LinkCellsVertex* const vvertexp = itp->cast<LinkCellsVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (const LinkCellsVertex* const vvertexp = vtx.cast<LinkCellsVertex>()) {
|
||||
// +1 so we leave level 1 for the new wrapper we'll make in a moment
|
||||
AstNodeModule* const modp = vvertexp->modp();
|
||||
modp->level(vvertexp->rank() + 1);
|
||||
|
|
|
|||
418
src/V3List.h
418
src/V3List.h
|
|
@ -1,6 +1,6 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: List class with storage in existing classes
|
||||
// DESCRIPTION: Verilator: Doubly linked endogenous (intrusive) list
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
|
|
@ -20,124 +20,324 @@
|
|||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <vector>
|
||||
#include "V3Error.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
//============================================================================
|
||||
// The list links (just 2 pointers), to be instantiated as a member in the
|
||||
// list element base class 'T_Base'. They are considered mutable, even if the
|
||||
// list element is 'const', as they are only used for storing the elements in
|
||||
// a V3List. That is, you can store const elements in a V3List.
|
||||
template <typename T_Base>
|
||||
class V3ListLinks final {
|
||||
// The V3List itself, but nothing else can access the link pointers
|
||||
template <typename B, V3ListLinks<B>& (B::*)(), typename>
|
||||
friend class V3List;
|
||||
|
||||
template <class T>
|
||||
class V3List;
|
||||
template <class T>
|
||||
class V3ListEnt;
|
||||
|
||||
template <class T>
|
||||
class V3List final {
|
||||
// List container for linked list of elements of type *T (T is a pointer type)
|
||||
private:
|
||||
// MEMBERS
|
||||
T m_headp = nullptr; // First element
|
||||
T m_tailp = nullptr; // Last element
|
||||
friend class V3ListEnt<T>;
|
||||
T_Base* m_nextp = nullptr; // Next element in list
|
||||
T_Base* m_prevp = nullptr; // Previous element in list
|
||||
|
||||
public:
|
||||
V3List() = default;
|
||||
~V3List() = default;
|
||||
// METHODS
|
||||
T begin() const { return m_headp; }
|
||||
T end() const { return nullptr; }
|
||||
T rbegin() const { return m_tailp; }
|
||||
T rend() const { return nullptr; }
|
||||
bool empty() const { return m_headp == nullptr; }
|
||||
void reset() { // clear() without walking the list
|
||||
m_headp = nullptr;
|
||||
m_tailp = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
template <class T>
|
||||
class V3ListEnt final {
|
||||
// List entry for linked list of elements of type *T (T is a pointer type)
|
||||
private:
|
||||
// MEMBERS
|
||||
T m_nextp = nullptr; // Pointer to next element, nullptr=end
|
||||
T m_prevp = nullptr; // Pointer to previous element, nullptr=beginning
|
||||
friend class V3List<T>;
|
||||
static V3ListEnt* baseToListEnt(void* newbasep, size_t offset) {
|
||||
// "this" must be an element inside of *basep
|
||||
// Use that to determine a structure offset, then apply to the new base
|
||||
// to get our new pointer information
|
||||
return (V3ListEnt*)(((uint8_t*)newbasep) + offset);
|
||||
}
|
||||
|
||||
public:
|
||||
V3ListEnt() = default;
|
||||
~V3ListEnt() {
|
||||
V3ListLinks() = default;
|
||||
~V3ListLinks() {
|
||||
#ifdef VL_DEBUG
|
||||
// Load bogus pointers so we can catch deletion bugs
|
||||
m_nextp = reinterpret_cast<T>(1);
|
||||
m_prevp = reinterpret_cast<T>(1);
|
||||
m_nextp = reinterpret_cast<T_Base*>(1);
|
||||
m_prevp = reinterpret_cast<T_Base*>(1);
|
||||
#endif
|
||||
}
|
||||
T nextp() const { return m_nextp; }
|
||||
T prevp() const { return m_prevp; }
|
||||
// METHODS
|
||||
void pushBack(V3List<T>& listr, T newp) {
|
||||
// "this" must be an element inside of *newp
|
||||
// cppcheck-suppress thisSubtraction
|
||||
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp);
|
||||
m_nextp = nullptr;
|
||||
if (!listr.m_headp) listr.m_headp = newp;
|
||||
m_prevp = listr.m_tailp;
|
||||
if (m_prevp) baseToListEnt(m_prevp, offset)->m_nextp = newp;
|
||||
listr.m_tailp = newp;
|
||||
}
|
||||
void pushFront(V3List<T>& listr, T newp) {
|
||||
// "this" must be an element inside of *newp
|
||||
// cppcheck-suppress thisSubtraction
|
||||
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp);
|
||||
m_nextp = listr.m_headp;
|
||||
if (m_nextp) baseToListEnt(m_nextp, offset)->m_prevp = newp;
|
||||
listr.m_headp = newp;
|
||||
m_prevp = nullptr;
|
||||
if (!listr.m_tailp) listr.m_tailp = newp;
|
||||
}
|
||||
// Unlink from side
|
||||
void unlink(V3List<T>& listr, T oldp) {
|
||||
// "this" must be an element inside of *oldp
|
||||
// cppcheck-suppress thisSubtraction
|
||||
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(oldp);
|
||||
if (m_nextp) {
|
||||
baseToListEnt(m_nextp, offset)->m_prevp = m_prevp;
|
||||
} else {
|
||||
listr.m_tailp = m_prevp;
|
||||
}
|
||||
if (m_prevp) {
|
||||
baseToListEnt(m_prevp, offset)->m_nextp = m_nextp;
|
||||
} else {
|
||||
listr.m_headp = m_nextp;
|
||||
}
|
||||
m_prevp = m_nextp = nullptr;
|
||||
}
|
||||
// Remove all nodes from 'oldListr', append them to 'newListr'. 'this' must be a member of the
|
||||
// object at 'selfp', and 'selfp' must be the head of the list in 'oldListr'.
|
||||
void moveAppend(V3List<T>& oldListr, V3List<T>& newListr, T selfp) {
|
||||
UASSERT(selfp == oldListr.m_headp, "Must be head of list to use 'moveAppend'");
|
||||
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(selfp);
|
||||
const T headp = selfp;
|
||||
const T tailp = oldListr.m_tailp;
|
||||
oldListr.reset();
|
||||
if (newListr.empty()) {
|
||||
newListr.m_headp = headp;
|
||||
newListr.m_tailp = tailp;
|
||||
} else {
|
||||
baseToListEnt(newListr.m_tailp, offset)->m_nextp = headp;
|
||||
m_prevp = newListr.m_tailp;
|
||||
newListr.m_tailp = tailp;
|
||||
}
|
||||
}
|
||||
VL_UNCOPYABLE(V3ListLinks);
|
||||
VL_UNMOVABLE(V3ListLinks);
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
// Generic endogenous (or intrusive) doubly linked list, with links stored
|
||||
// inside the elements. The template parameters are:
|
||||
// - 'T_Base' is the base type of elements that contains the V3ListLinks
|
||||
// instance as a data member.
|
||||
// - 'LinksGetter' is a member function pointer that returns a reference to
|
||||
// the links within 'T_Base'. Note that you should be able to use a data
|
||||
// member pointer, instead of a function pointer, but that is buggy in MSVC.
|
||||
// - 'T_Element' is the actual type of elements, which must be the same,
|
||||
// or a subtype of 'T_Base'.
|
||||
template <typename T_Base, //
|
||||
V3ListLinks<T_Base>& (T_Base::*LinksGetter)(), //
|
||||
typename T_Element = T_Base>
|
||||
class V3List final {
|
||||
static_assert(std::is_base_of<T_Base, T_Element>::value,
|
||||
"'T_Element' must be a subtype of 'T_Base");
|
||||
|
||||
using ListType = V3List<T_Base, LinksGetter, T_Element>;
|
||||
|
||||
// MEMBERS
|
||||
T_Base* m_headp = nullptr;
|
||||
T_Base* m_lastp = nullptr;
|
||||
|
||||
// Given the T_Base, return the Links. The links are always mutable, even in const elements.
|
||||
VL_ATTR_ALWINLINE
|
||||
static V3ListLinks<T_Base>& toLinks(const T_Base* elementp) {
|
||||
return (const_cast<T_Base*>(elementp)->*LinksGetter)();
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
static void prefetch(T_Base* elementp, T_Base* fallbackp) {
|
||||
UDEBUGONLY(UASSERT(fallbackp, "Prefetch fallback pointer must be non nullptr"););
|
||||
// This compiles to a branchless prefetch with cmove, with the address always valid
|
||||
VL_PREFETCH_RW(elementp ? elementp : fallbackp);
|
||||
}
|
||||
|
||||
// Iterator class template for V3List. This is just enough to support range based for loops
|
||||
// and basic usage. Feel free to extend as required.
|
||||
template <typename T_IteratorElement, bool T_Reverse>
|
||||
class SimpleItertatorImpl final {
|
||||
static_assert(std::is_same<T_IteratorElement, T_Element>::value
|
||||
|| std::is_same<T_IteratorElement, const T_Element>::value,
|
||||
"'SimpleItertatorImpl' must be used with element type only");
|
||||
|
||||
// The List itself, but nothing else can construct iterators
|
||||
template <typename B, V3ListLinks<B>& (B::*)(), typename>
|
||||
friend class V3List;
|
||||
|
||||
using IteratorType = SimpleItertatorImpl<T_IteratorElement, T_Reverse>;
|
||||
|
||||
T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
SimpleItertatorImpl(T_Base* elementp)
|
||||
: m_currp{elementp} {}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
static T_Base* step(T_Base* currp) {
|
||||
if VL_CONSTEXPR_CXX17 (T_Reverse) {
|
||||
return toLinks(currp).m_prevp;
|
||||
} else {
|
||||
return toLinks(currp).m_nextp;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// Dereference
|
||||
VL_ATTR_ALWINLINE
|
||||
T_IteratorElement& operator*() const {
|
||||
UDEBUGONLY(UASSERT(m_currp, "Dereferencing end of list iterator"););
|
||||
prefetch(step(m_currp), m_currp);
|
||||
return *static_cast<T_IteratorElement*>(m_currp);
|
||||
}
|
||||
// Pre increment
|
||||
VL_ATTR_ALWINLINE
|
||||
IteratorType& operator++() {
|
||||
UDEBUGONLY(UASSERT(m_currp, "Pre-incrementing end of list iterator"););
|
||||
m_currp = step(m_currp);
|
||||
return *this;
|
||||
}
|
||||
// Post increment
|
||||
VL_ATTR_ALWINLINE
|
||||
IteratorType operator++(int) {
|
||||
UDEBUGONLY(UASSERT(m_currp, "Post-incrementing end of list iterator"););
|
||||
T_Base* const elementp = m_currp;
|
||||
m_currp = step(m_currp);
|
||||
return IteratorType{elementp};
|
||||
}
|
||||
VL_ATTR_ALWINLINE
|
||||
bool operator==(const IteratorType& other) const { return m_currp == other.m_currp; }
|
||||
VL_ATTR_ALWINLINE
|
||||
bool operator!=(const IteratorType& other) const { return m_currp != other.m_currp; }
|
||||
// Convert to const iterator
|
||||
VL_ATTR_ALWINLINE
|
||||
operator SimpleItertatorImpl<const T_IteratorElement, T_Reverse>() const {
|
||||
return SimpleItertatorImpl<const T_IteratorElement, T_Reverse>{m_currp};
|
||||
}
|
||||
};
|
||||
|
||||
// Proxy class for creating unlinkable iterators, so we can use
|
||||
// 'for (T_Element* const ptr : list.unlinkable()) list.unlink(ptr);'
|
||||
class UnlinkableProxy final {
|
||||
// The List itself, but nothing else can construct UnlinkableProxy
|
||||
template <typename B, V3ListLinks<B>& (B::*)(), typename>
|
||||
friend class V3List;
|
||||
|
||||
ListType& m_list; // The proxied list
|
||||
|
||||
UnlinkableProxy(ListType& list)
|
||||
: m_list{list} {}
|
||||
|
||||
// Unlinkable iterator class template. This only supports enough for range based for loops.
|
||||
// If you want something fancier, use and manage the direct iterator manually.
|
||||
template <typename T_IteratorElement>
|
||||
class UnlinkableItertatorImpl final {
|
||||
static_assert(std::is_same<T_IteratorElement, T_Element>::value
|
||||
|| std::is_same<T_IteratorElement, const T_Element>::value,
|
||||
"'UnlinkableItertatorImpl' must be used with element type only");
|
||||
|
||||
// The UnlinkableProxy, but nothing else can construct unlinkable iterators
|
||||
friend class UnlinkableProxy;
|
||||
|
||||
using IteratorType = UnlinkableItertatorImpl<T_IteratorElement>;
|
||||
|
||||
T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator
|
||||
T_Base* m_nextp; // Next element after current, or 'nullptr' for 'end()' iterator
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
UnlinkableItertatorImpl(T_Base* elementp)
|
||||
: m_currp{elementp}
|
||||
, m_nextp{toLinks(m_currp).m_nextp} {}
|
||||
VL_ATTR_ALWINLINE
|
||||
UnlinkableItertatorImpl(std::nullptr_t)
|
||||
: m_currp{nullptr}
|
||||
, m_nextp{nullptr} {}
|
||||
|
||||
public:
|
||||
// Dereference - Note this returns a pointer.
|
||||
VL_ATTR_ALWINLINE
|
||||
T_IteratorElement* operator*() const {
|
||||
UDEBUGONLY(UASSERT(m_currp, "Dereferencing end of list iterator"););
|
||||
prefetch(m_nextp, m_currp);
|
||||
return static_cast<T_IteratorElement*>(m_currp);
|
||||
}
|
||||
// Pre increment - Keeps hold of current next pointer.
|
||||
VL_ATTR_ALWINLINE
|
||||
IteratorType& operator++() {
|
||||
UDEBUGONLY(UASSERT(m_currp, "Pre-incrementing end of list iterator"););
|
||||
m_currp = m_nextp;
|
||||
m_nextp = m_currp ? toLinks(m_currp).m_nextp : nullptr;
|
||||
return *this;
|
||||
}
|
||||
VL_ATTR_ALWINLINE
|
||||
bool operator!=(const IteratorType& other) const { return m_currp != other.m_currp; }
|
||||
};
|
||||
|
||||
public:
|
||||
using iterator = UnlinkableItertatorImpl<T_Element>;
|
||||
using const_iterator = UnlinkableItertatorImpl<const T_Element>;
|
||||
iterator begin() { //
|
||||
return m_list.m_headp ? iterator{m_list.m_headp} : end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return m_list.m_headp ? const_iterator{m_list.m_headp} : end();
|
||||
}
|
||||
iterator end() { return iterator{nullptr}; }
|
||||
const_iterator end() const { return const_iterator{nullptr}; }
|
||||
};
|
||||
|
||||
public:
|
||||
using iterator = SimpleItertatorImpl<T_Element, /* T_Reverse: */ false>;
|
||||
using const_iterator = SimpleItertatorImpl<const T_Element, /* T_Reverse: */ false>;
|
||||
using reverse_iterator = SimpleItertatorImpl<T_Element, /* T_Reverse: */ true>;
|
||||
using const_reverse_iterator = SimpleItertatorImpl<const T_Element, /* T_Reverse: */ true>;
|
||||
|
||||
// CONSTRUCTOR
|
||||
V3List() = default;
|
||||
~V3List() {
|
||||
#ifdef VL_DEBUG
|
||||
m_headp = reinterpret_cast<T_Element*>(1);
|
||||
m_lastp = reinterpret_cast<T_Element*>(1);
|
||||
#endif
|
||||
}
|
||||
VL_UNCOPYABLE(V3List);
|
||||
VL_UNMOVABLE(V3List);
|
||||
|
||||
// METHDOS
|
||||
bool empty() const { return !m_headp; }
|
||||
bool hasSingleElement() const { return m_headp && m_headp == m_lastp; }
|
||||
bool hasMultipleElements() const { return m_headp && m_headp != m_lastp; }
|
||||
|
||||
// These return pointers, as we often want to unlink/delete them, and can also signal empty
|
||||
T_Element* frontp() { return static_cast<T_Element*>(m_headp); }
|
||||
const T_Element* frontp() const { return static_cast<T_Element*>(m_headp); }
|
||||
T_Element* backp() { return static_cast<T_Element*>(m_lastp); }
|
||||
const T_Element* backp() const { return static_cast<T_Element*>(m_lastp); }
|
||||
|
||||
// Standard iterators. The iterator is only invalidated if the element it points to is
|
||||
// unlinked. Other list operations do not invalidate the itartor. If you want to be able to
|
||||
// unlink the currently iterated element, use 'unlinkable()' below.
|
||||
iterator begin() { return iterator{m_headp}; }
|
||||
const_iterator begin() const { return const_iterator{m_headp}; }
|
||||
iterator end() { return iterator{nullptr}; }
|
||||
const_iterator end() const { return const_iterator{nullptr}; }
|
||||
reverse_iterator rbegin() { return reverse_iterator{m_lastp}; }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator{m_lastp}; }
|
||||
reverse_iterator rend() { return reverse_iterator{nullptr}; }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator{nullptr}; }
|
||||
|
||||
// Handle to create unlinkable iterators, which allows unlinking the currently iterated
|
||||
// element without invalidating the iterator. However, every other operation that mutates
|
||||
// the list invalidates the unlinkable iterator!
|
||||
UnlinkableProxy unlinkable() { return UnlinkableProxy{*this}; }
|
||||
|
||||
// Link (insert) existing element at front
|
||||
void linkFront(const T_Element* elementp) {
|
||||
auto& links = toLinks(elementp);
|
||||
links.m_nextp = m_headp;
|
||||
links.m_prevp = nullptr;
|
||||
if (m_headp) toLinks(m_headp).m_prevp = const_cast<T_Element*>(elementp);
|
||||
m_headp = const_cast<T_Element*>(elementp);
|
||||
if (!m_lastp) m_lastp = m_headp;
|
||||
}
|
||||
|
||||
// Link (insert) existing element at back
|
||||
void linkBack(const T_Element* elementp) {
|
||||
auto& links = toLinks(elementp);
|
||||
links.m_nextp = nullptr;
|
||||
links.m_prevp = m_lastp;
|
||||
if (m_lastp) toLinks(m_lastp).m_nextp = const_cast<T_Element*>(elementp);
|
||||
m_lastp = const_cast<T_Element*>(elementp);
|
||||
if (!m_headp) m_headp = m_lastp;
|
||||
}
|
||||
|
||||
// Unlink (remove) and return element at the front. Returns 'nullptr' if the list is empty.
|
||||
T_Element* unlinkFront() {
|
||||
T_Element* const headp = m_headp;
|
||||
if (headp) unlink(headp);
|
||||
return headp;
|
||||
}
|
||||
|
||||
// Unlink (remove) and return element at the back. Returns 'nullptr' if the list is empty.
|
||||
T_Element* unlinkBack() {
|
||||
T_Element* const lastp = m_lastp;
|
||||
if (lastp) unlink(lastp);
|
||||
return lastp;
|
||||
}
|
||||
|
||||
// Unlink (remove) the given element.
|
||||
void unlink(const T_Element* elementp) {
|
||||
auto& links = toLinks(elementp);
|
||||
if (links.m_nextp) toLinks(links.m_nextp).m_prevp = links.m_prevp;
|
||||
if (links.m_prevp) toLinks(links.m_prevp).m_nextp = links.m_nextp;
|
||||
if (m_headp == elementp) m_headp = links.m_nextp;
|
||||
if (m_lastp == elementp) m_lastp = links.m_prevp;
|
||||
links.m_prevp = nullptr;
|
||||
links.m_nextp = nullptr;
|
||||
}
|
||||
|
||||
// Swap elements of 2 lists
|
||||
void swap(ListType& other) {
|
||||
std::swap(m_headp, other.m_headp);
|
||||
std::swap(m_lastp, other.m_lastp);
|
||||
}
|
||||
|
||||
// Take elements from 'other' and link (insert) them all before the given position.
|
||||
void splice(const_iterator pos, ListType& other) {
|
||||
if (empty()) {
|
||||
swap(other);
|
||||
} else if (other.empty()) {
|
||||
return;
|
||||
} else {
|
||||
UASSERT(pos == end(), "Sorry, only splicing at the end is implemented at the moment");
|
||||
toLinks(m_lastp).m_nextp = other.m_headp;
|
||||
toLinks(other.m_headp).m_prevp = m_lastp;
|
||||
m_lastp = other.m_lastp;
|
||||
other.m_headp = nullptr;
|
||||
other.m_lastp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// This is O(n)!
|
||||
size_t size() const {
|
||||
size_t result = 0;
|
||||
for (auto it = begin(); it != end(); ++it) ++result;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -52,12 +52,12 @@ void processDomains(AstNetlist* netlistp, //
|
|||
const TrigToSenMap& trigToSen, //
|
||||
const ExternalDomainsProvider& externalDomains);
|
||||
|
||||
std::vector<AstActive*> createSerial(const OrderGraph& orderGraph, //
|
||||
std::vector<AstActive*> createSerial(OrderGraph& orderGraph, //
|
||||
const std::string& tag, //
|
||||
const TrigToSenMap& trigToSenMap, //
|
||||
bool slow);
|
||||
|
||||
AstExecGraph* createParallel(const OrderGraph& orderGraph, //
|
||||
AstExecGraph* createParallel(OrderGraph& orderGraph, //
|
||||
const std::string& tag, //
|
||||
const TrigToSenMap& trigToSenMap, //
|
||||
bool slow);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class OrderMoveGraphBuilder final {
|
|||
using DomainMap = std::map<const AstSenTree*, OrderMoveVertex*>;
|
||||
|
||||
// MEMBERS
|
||||
const OrderGraph& m_orderGraph; // Input OrderGraph
|
||||
OrderGraph& m_orderGraph; // Input OrderGraph
|
||||
std::unique_ptr<OrderMoveGraph> m_moveGraphp{new OrderMoveGraph}; // Output OrderMoveGraph
|
||||
// Map from Trigger reference AstSenItem to the original AstSenTree
|
||||
const V3Order::TrigToSenMap& m_trigToSen;
|
||||
|
|
@ -62,7 +62,7 @@ class OrderMoveGraphBuilder final {
|
|||
std::deque<DomainMap> m_domainMaps;
|
||||
|
||||
// CONSTRUCTORS
|
||||
OrderMoveGraphBuilder(const OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen)
|
||||
OrderMoveGraphBuilder(OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen)
|
||||
: m_orderGraph{orderGraph}
|
||||
, m_trigToSen{trigToSen} {
|
||||
// How this works:
|
||||
|
|
@ -77,18 +77,18 @@ class OrderMoveGraphBuilder final {
|
|||
|
||||
// For each logic vertex, make a OrderMoveVertex, for each variable vertex, allocate
|
||||
// storage
|
||||
for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (OrderLogicVertex* const lvtxp = itp->cast<OrderLogicVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_orderGraph.vertices()) {
|
||||
if (OrderLogicVertex* const lvtxp = vtx.cast<OrderLogicVertex>()) {
|
||||
lvtxp->userp(new OrderMoveVertex{*m_moveGraphp, lvtxp, lvtxp->domainp()});
|
||||
} else {
|
||||
// This is an OrderVarVertex
|
||||
m_domainMaps.emplace_back();
|
||||
itp->userp(&m_domainMaps.back());
|
||||
vtx.userp(&m_domainMaps.back());
|
||||
}
|
||||
}
|
||||
// Build edges between logic vertices
|
||||
for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (OrderLogicVertex* const lvtxp = itp->cast<OrderLogicVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_orderGraph.vertices()) {
|
||||
if (OrderLogicVertex* const lvtxp = vtx.cast<OrderLogicVertex>()) {
|
||||
iterateLogicVertex(lvtxp);
|
||||
}
|
||||
}
|
||||
|
|
@ -167,11 +167,11 @@ class OrderMoveGraphBuilder final {
|
|||
AstSenTree* const domainp = lvtxp->domainp();
|
||||
OrderMoveVertex* const lMoveVtxp = static_cast<OrderMoveVertex*>(lvtxp->userp());
|
||||
// Search forward from lvtxp, making new edges from lMoveVtxp forward
|
||||
for (V3GraphEdge* edgep = lvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() == 0) continue; // Was cut
|
||||
for (const V3GraphEdge& edge : lvtxp->outEdges()) {
|
||||
if (edge.weight() == 0) continue; // Was cut
|
||||
|
||||
// OrderGraph is a bipartite graph, so we know it's an OrderVarVertex
|
||||
const OrderVarVertex* const vvtxp = static_cast<const OrderVarVertex*>(edgep->top());
|
||||
const OrderVarVertex* const vvtxp = static_cast<const OrderVarVertex*>(edge.top());
|
||||
|
||||
// Look up OrderMoveVertex for this domain on this variable
|
||||
DomainMap& mapp = *static_cast<DomainMap*>(vvtxp->userp());
|
||||
|
|
@ -196,11 +196,11 @@ class OrderMoveGraphBuilder final {
|
|||
OrderMoveVertex* iterateVarVertex(const OrderVarVertex* vvtxp, AstSenTree* domainp) {
|
||||
OrderMoveVertex* vMoveVtxp = nullptr;
|
||||
// Search forward from vvtxp, making new edges from vMoveVtxp forward
|
||||
for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() == 0) continue; // Was cut
|
||||
for (const V3GraphEdge& edge : vvtxp->outEdges()) {
|
||||
if (edge.weight() == 0) continue; // Was cut
|
||||
|
||||
// OrderGraph is a bipartite graph, so we know it's an OrderLogicVertex
|
||||
const OrderLogicVertex* const lVtxp = edgep->top()->as<OrderLogicVertex>();
|
||||
const OrderLogicVertex* const lVtxp = edge.top()->as<OrderLogicVertex>();
|
||||
|
||||
// Do not construct dependencies across exclusive domains.
|
||||
if (domainsExclusive(domainp, lVtxp->domainp())) continue;
|
||||
|
|
@ -214,7 +214,7 @@ class OrderMoveGraphBuilder final {
|
|||
}
|
||||
|
||||
public:
|
||||
static std::unique_ptr<OrderMoveGraph> apply(const OrderGraph& orderGraph,
|
||||
static std::unique_ptr<OrderMoveGraph> apply(OrderGraph& orderGraph,
|
||||
const V3Order::TrigToSenMap& trigToSen) {
|
||||
return std::move(OrderMoveGraphBuilder{orderGraph, trigToSen}.m_moveGraphp);
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ public:
|
|||
//======================================================================
|
||||
// OrderMoveGraph implementation
|
||||
|
||||
std::unique_ptr<OrderMoveGraph> OrderMoveGraph::build(const OrderGraph& orderGraph,
|
||||
std::unique_ptr<OrderMoveGraph> OrderMoveGraph::build(OrderGraph& orderGraph,
|
||||
const V3Order::TrigToSenMap& trigToSen) {
|
||||
return OrderMoveGraphBuilder::apply(orderGraph, trigToSen);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,15 +29,71 @@
|
|||
#include "V3Order.h"
|
||||
#include "V3OrderGraph.h"
|
||||
|
||||
class OrderMoveVertex;
|
||||
class OrderMoveDomScope;
|
||||
class OrderMoveGraph;
|
||||
|
||||
//======================================================================
|
||||
// Vertex types
|
||||
|
||||
class OrderMoveVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex)
|
||||
|
||||
// The corresponding logic vertex, or nullptr if this MoveVertex stands for a variable vertex.
|
||||
OrderLogicVertex* const m_logicp;
|
||||
OrderMoveDomScope& m_domScope; // DomScope this vertex is under
|
||||
V3ListLinks<OrderMoveVertex> m_links; // List links to store instances of this class
|
||||
|
||||
// METHODS
|
||||
std::string dotColor() const override { return logicp() ? logicp()->dotColor() : "yellow"; }
|
||||
|
||||
std::string name() const override VL_MT_STABLE {
|
||||
std::string nm;
|
||||
if (!logicp()) {
|
||||
nm = "var";
|
||||
} else {
|
||||
nm = logicp()->name() + "\\n";
|
||||
nm += "MV:";
|
||||
nm += +" d=" + cvtToHex(logicp()->domainp());
|
||||
nm += +" s=" + cvtToHex(logicp()->scopep());
|
||||
}
|
||||
if (userp()) nm += "\nu=" + cvtToHex(userp());
|
||||
return nm;
|
||||
}
|
||||
|
||||
V3ListLinks<OrderMoveVertex>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List type to store instances of this class
|
||||
using List = V3List<OrderMoveVertex, &OrderMoveVertex::links>;
|
||||
|
||||
// CONSTRUCTORS
|
||||
OrderMoveVertex(OrderMoveGraph& graph, OrderLogicVertex* lVtxp,
|
||||
const AstSenTree* domainp) VL_MT_DISABLED;
|
||||
~OrderMoveVertex() override = default;
|
||||
VL_UNCOPYABLE(OrderMoveVertex);
|
||||
|
||||
OrderLogicVertex* logicp() const VL_MT_STABLE { return m_logicp; }
|
||||
OrderMoveDomScope& domScope() const { return m_domScope; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Graph type
|
||||
|
||||
// OrderMoveGraph is constructed from the fine-grained OrderGraph.
|
||||
// It is a slightly coarsened representation of dependencies used to drive serialization.
|
||||
class OrderMoveGraph final : public V3Graph {
|
||||
public:
|
||||
// Build an OrderMoveGraph from an OrderGraph
|
||||
static std::unique_ptr<OrderMoveGraph> build(OrderGraph&, const V3Order::TrigToSenMap&);
|
||||
};
|
||||
|
||||
// Information stored for each unique (domain, scope) pair. Mainly a list of ready vertices under
|
||||
// that (domain, scope). OrderMoveDomScope instances are themselves organized into a global ready
|
||||
// list if they have ready vertices.
|
||||
class OrderMoveDomScope final {
|
||||
// STATE
|
||||
V3List<OrderMoveVertex*> m_readyVertices; // Ready vertices in this domain/scope
|
||||
V3ListEnt<OrderMoveDomScope*> m_listEnt; // List entry to store this instance
|
||||
OrderMoveVertex::List m_readyVertices; // Ready vertices in this domain/scope
|
||||
V3ListLinks<OrderMoveDomScope> m_links; // List links to store instances of this class
|
||||
bool m_isOnList = false; // True if DomScope is already on a list through m_listEnt
|
||||
const AstSenTree* const m_domainp; // Domain the vertices belong to
|
||||
const AstScope* const m_scopep; // Scope the vertices belong to
|
||||
|
|
@ -72,7 +128,12 @@ class OrderMoveDomScope final {
|
|||
// Map from Domain/Scope to the corresponding DomScope instance
|
||||
static DomScopeMap s_dsMap;
|
||||
|
||||
V3ListLinks<OrderMoveDomScope>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List type to store instances of this class
|
||||
using List = V3List<OrderMoveDomScope, &OrderMoveDomScope::links>;
|
||||
|
||||
// STATIC MEMBERS
|
||||
static OrderMoveDomScope& getOrCreate(const AstSenTree* domainp, const AstScope* scopep) {
|
||||
return s_dsMap
|
||||
|
|
@ -92,84 +153,12 @@ public:
|
|||
VL_UNMOVABLE(OrderMoveDomScope);
|
||||
|
||||
// MEMBERS
|
||||
V3List<OrderMoveVertex*>& readyVertices() { return m_readyVertices; }
|
||||
OrderMoveVertex::List& readyVertices() { return m_readyVertices; }
|
||||
const AstSenTree* domainp() const { return m_domainp; }
|
||||
const AstScope* scopep() const { return m_scopep; }
|
||||
|
||||
bool isOnList() const { return m_isOnList; }
|
||||
void unlinkFrom(V3List<OrderMoveDomScope*>& list) {
|
||||
UASSERT_OBJ(m_isOnList, m_domainp, "unlinkFrom, but DomScope is not on a list");
|
||||
m_isOnList = false;
|
||||
m_listEnt.unlink(list, this);
|
||||
}
|
||||
void appendTo(V3List<OrderMoveDomScope*>& list) {
|
||||
UASSERT_OBJ(!m_isOnList, m_domainp, "appendTo, but DomScope is already on a list");
|
||||
m_isOnList = true;
|
||||
m_listEnt.pushBack(list, this);
|
||||
}
|
||||
void prependTo(V3List<OrderMoveDomScope*>& list) {
|
||||
UASSERT_OBJ(!m_isOnList, m_domainp, "prependTo, but DomScope is already on a list");
|
||||
m_isOnList = true;
|
||||
m_listEnt.pushFront(list, this);
|
||||
}
|
||||
OrderMoveDomScope* nextp() const { return m_listEnt.nextp(); }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Graph type
|
||||
|
||||
// OrderMoveGraph is constructed from the fine-grained OrderGraph.
|
||||
// It is a slightly coarsened representation of dependencies used to drive serialization.
|
||||
class OrderMoveGraph final : public V3Graph {
|
||||
public:
|
||||
// Build an OrderMoveGraph from an OrderGraph
|
||||
static std::unique_ptr<OrderMoveGraph> build(const OrderGraph&, const V3Order::TrigToSenMap&);
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Vertex types
|
||||
|
||||
class OrderMoveVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex)
|
||||
|
||||
// The corresponding logic vertex, or nullptr if this MoveVertex stands for a variable vertex.
|
||||
OrderLogicVertex* const m_logicp;
|
||||
OrderMoveDomScope& m_domScope; // DomScope this vertex is under
|
||||
V3ListEnt<OrderMoveVertex*> m_listEnt; // List entry to store this Vertex
|
||||
|
||||
// METHODS
|
||||
std::string dotColor() const override { return logicp() ? logicp()->dotColor() : "yellow"; }
|
||||
|
||||
std::string name() const override VL_MT_STABLE {
|
||||
std::string nm;
|
||||
if (!logicp()) {
|
||||
nm = "var";
|
||||
} else {
|
||||
nm = logicp()->name() + "\\n";
|
||||
nm += "MV:";
|
||||
nm += +" d=" + cvtToHex(logicp()->domainp());
|
||||
nm += +" s=" + cvtToHex(logicp()->scopep());
|
||||
}
|
||||
if (userp()) nm += "\nu=" + cvtToHex(userp());
|
||||
return nm;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
OrderMoveVertex(OrderMoveGraph& graph, OrderLogicVertex* lVtxp,
|
||||
const AstSenTree* domainp) VL_MT_DISABLED;
|
||||
~OrderMoveVertex() override = default;
|
||||
VL_UNCOPYABLE(OrderMoveVertex);
|
||||
|
||||
OrderLogicVertex* logicp() const VL_MT_STABLE { return m_logicp; }
|
||||
OrderMoveDomScope& domScope() const { return m_domScope; }
|
||||
|
||||
void unlinkFrom(V3List<OrderMoveVertex*>& list) { m_listEnt.unlink(list, this); }
|
||||
void appendTo(V3List<OrderMoveVertex*>& list) { m_listEnt.pushBack(list, this); }
|
||||
void moveAppend(V3List<OrderMoveVertex*>& src, V3List<OrderMoveVertex*>& dst) {
|
||||
m_listEnt.moveAppend(src, dst, this);
|
||||
}
|
||||
OrderMoveVertex* nextp() const { return m_listEnt.nextp(); }
|
||||
void isOnList(bool value) { m_isOnList = value; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
|
@ -177,7 +166,7 @@ public:
|
|||
|
||||
class OrderMoveGraphSerializer final {
|
||||
// STATE
|
||||
V3List<OrderMoveDomScope*> m_readyDomScopeps; // List of DomScopes which have ready vertices
|
||||
OrderMoveDomScope::List m_readyDomScopeps; // List of DomScopes which have ready vertices
|
||||
OrderMoveDomScope* m_nextDomScopep = nullptr; // Next DomScope to yield from
|
||||
|
||||
// METHODS
|
||||
|
|
@ -187,16 +176,18 @@ class OrderMoveGraphSerializer final {
|
|||
if (vtxp->logicp()) {
|
||||
// Add this vertex to the ready list of its DomScope
|
||||
OrderMoveDomScope& domScope = vtxp->domScope();
|
||||
vtxp->appendTo(domScope.readyVertices());
|
||||
domScope.readyVertices().linkBack(vtxp);
|
||||
// Add the DomScope to the global ready list if not there yet
|
||||
if (!domScope.isOnList()) domScope.appendTo(m_readyDomScopeps);
|
||||
if (!domScope.isOnList()) {
|
||||
domScope.isOnList(true);
|
||||
m_readyDomScopeps.linkBack(&domScope);
|
||||
}
|
||||
} else { // This is a bit nonsense at this point, but equivalent to the old version
|
||||
// Remove dependency on vertex we are returning. This might add vertices to
|
||||
// currReadyList.
|
||||
for (V3GraphEdge *edgep = vtxp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
// The dependent variable
|
||||
OrderMoveVertex* const dVtxp = edgep->top()->as<OrderMoveVertex>();
|
||||
OrderMoveVertex* const dVtxp = edge.top()->as<OrderMoveVertex>();
|
||||
// Update number of dependencies
|
||||
const uint32_t nDeps = dVtxp->user() - 1;
|
||||
dVtxp->user(nDeps);
|
||||
|
|
@ -210,11 +201,9 @@ public:
|
|||
// CONSTRUCTOR
|
||||
OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
|
||||
// Set V3GraphVertex::user() to the number of incoming edges (upstream dependencies)
|
||||
for (V3GraphVertex *vtxp = moveGraph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNextp();
|
||||
uint32_t nDeps = 0;
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) ++nDeps;
|
||||
vtxp->user(nDeps);
|
||||
for (V3GraphVertex& vtx : moveGraph.vertices()) {
|
||||
const uint32_t nDeps = vtx.inEdges().size();
|
||||
vtx.user(nDeps);
|
||||
}
|
||||
}
|
||||
~OrderMoveGraphSerializer() = default;
|
||||
|
|
@ -224,26 +213,27 @@ public:
|
|||
void addSeed(OrderMoveVertex* vtxp) { ready(vtxp); }
|
||||
|
||||
OrderMoveVertex* getNext() {
|
||||
if (!m_nextDomScopep) m_nextDomScopep = m_readyDomScopeps.begin();
|
||||
OrderMoveDomScope* const currDomScopep = m_nextDomScopep;
|
||||
if (!m_nextDomScopep) m_nextDomScopep = m_readyDomScopeps.frontp();
|
||||
// If nothing is ready, we are done
|
||||
if (!currDomScopep) return nullptr;
|
||||
if (!m_nextDomScopep) return nullptr;
|
||||
OrderMoveDomScope& currDomScope = *m_nextDomScopep;
|
||||
|
||||
V3List<OrderMoveVertex*>& currReadyList = currDomScopep->readyVertices();
|
||||
// This is the vertex we are returning now
|
||||
OrderMoveVertex* const mVtxp = currReadyList.begin();
|
||||
UASSERT(mVtxp, "DomScope on ready list, but has no ready vertices");
|
||||
// Unlink vertex from ready list under the DomScope
|
||||
mVtxp->unlinkFrom(currReadyList);
|
||||
OrderMoveVertex::List& currReadyList = currDomScope.readyVertices();
|
||||
UASSERT(!currReadyList.empty(), "DomScope on ready list, but has no ready vertices");
|
||||
|
||||
// Remove vertex from ready list under the DomScope. This is the vertex we are returning.
|
||||
OrderMoveVertex* mVtxp = currReadyList.unlinkFront();
|
||||
|
||||
// Nonsesne, but what we used to do
|
||||
if (currReadyList.empty()) currDomScopep->unlinkFrom(m_readyDomScopeps);
|
||||
if (currReadyList.empty()) {
|
||||
currDomScope.isOnList(false);
|
||||
m_readyDomScopeps.unlink(&currDomScope);
|
||||
}
|
||||
|
||||
// Remove dependency on vertex we are returning. This might add vertices to currReadyList.
|
||||
for (V3GraphEdge *edgep = mVtxp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge& edge : mVtxp->outEdges()) {
|
||||
// The dependent variable
|
||||
OrderMoveVertex* const dVtxp = edgep->top()->as<OrderMoveVertex>();
|
||||
OrderMoveVertex* const dVtxp = edge.top()->as<OrderMoveVertex>();
|
||||
// Update number of dependencies
|
||||
const uint32_t nDeps = dVtxp->user() - 1;
|
||||
dVtxp->user(nDeps);
|
||||
|
|
@ -255,9 +245,9 @@ public:
|
|||
// under the same domain.
|
||||
if (currReadyList.empty()) {
|
||||
m_nextDomScopep = nullptr;
|
||||
for (OrderMoveDomScope* dsp = m_readyDomScopeps.begin(); dsp; dsp = dsp->nextp()) {
|
||||
if (dsp->domainp() == currDomScopep->domainp()) {
|
||||
m_nextDomScopep = dsp;
|
||||
for (OrderMoveDomScope& domScope : m_readyDomScopeps) {
|
||||
if (domScope.domainp() == currDomScope.domainp()) {
|
||||
m_nextDomScopep = &domScope;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -83,8 +83,8 @@ class V3OrderProcessDomains final {
|
|||
// Buffer to hold external sensitivities
|
||||
std::vector<AstSenTree*> externalDomainps;
|
||||
// For each vertex
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
OrderEitherVertex* const vtxp = itp->as<OrderEitherVertex>();
|
||||
for (V3GraphVertex& it : m_graph.vertices()) {
|
||||
OrderEitherVertex* const vtxp = it.as<OrderEitherVertex>();
|
||||
UINFO(5, " pdi: " << vtxp << endl);
|
||||
// Sequential logic already has its domain set
|
||||
if (vtxp->domainp()) continue;
|
||||
|
|
@ -95,10 +95,10 @@ class V3OrderProcessDomains final {
|
|||
if (lvtxp) domainp = lvtxp->hybridp();
|
||||
|
||||
// For each incoming edge, examine the source vertex
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
OrderEitherVertex* const fromVtxp = edgep->fromp()->as<OrderEitherVertex>();
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
OrderEitherVertex* const fromVtxp = edge.fromp()->as<OrderEitherVertex>();
|
||||
// Cut edge
|
||||
if (!edgep->weight()) continue;
|
||||
if (!edge.weight()) continue;
|
||||
//
|
||||
if (!fromVtxp->domainMatters()) continue;
|
||||
|
||||
|
|
@ -162,14 +162,14 @@ class V3OrderProcessDomains final {
|
|||
std::unordered_map<VNRef<const AstSenItem>, const AstSenTree*> trigToSen;
|
||||
for (const auto& pair : m_trigToSen) trigToSen.emplace(*pair.first, pair.second);
|
||||
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (OrderVarVertex* const vvertexp = itp->cast<OrderVarVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (OrderVarVertex* const vvertexp = vtx.cast<OrderVarVertex>()) {
|
||||
string name(vvertexp->vscp()->prettyName());
|
||||
if (itp->is<OrderVarPreVertex>()) {
|
||||
if (vtx.is<OrderVarPreVertex>()) {
|
||||
name += " {PRE}";
|
||||
} else if (itp->is<OrderVarPostVertex>()) {
|
||||
} else if (vtx.is<OrderVarPostVertex>()) {
|
||||
name += " {POST}";
|
||||
} else if (itp->is<OrderVarPordVertex>()) {
|
||||
} else if (vtx.is<OrderVarPordVertex>()) {
|
||||
name += " {PORD}";
|
||||
}
|
||||
std::ostringstream os;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
//######################################################################
|
||||
// OrderSerial class
|
||||
|
||||
std::vector<AstActive*> V3Order::createSerial(const OrderGraph& graph, const std::string& tag,
|
||||
std::vector<AstActive*> V3Order::createSerial(OrderGraph& graph, const std::string& tag,
|
||||
const TrigToSenMap& trigToSen, bool slow) {
|
||||
|
||||
UINFO(2, " Constructing serial code for '" + tag + "'");
|
||||
|
|
@ -45,9 +45,8 @@ std::vector<AstActive*> V3Order::createSerial(const OrderGraph& graph, const std
|
|||
OrderMoveGraphSerializer serializer{*moveGraphp};
|
||||
|
||||
// Add initially ready vertices (those with no dependencies) to the serializer as seeds
|
||||
for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNextp();
|
||||
if (vtxp->inEmpty()) serializer.addSeed(vtxp->as<OrderMoveVertex>());
|
||||
for (V3GraphVertex& vtx : moveGraphp->vertices()) {
|
||||
if (vtx.inEmpty()) serializer.addSeed(vtx.as<OrderMoveVertex>());
|
||||
}
|
||||
|
||||
// Emit all logic as they become ready
|
||||
|
|
@ -68,8 +67,7 @@ std::vector<AstActive*> V3Order::createSerial(const OrderGraph& graph, const std
|
|||
}
|
||||
|
||||
// Delete the remaining variable vertices
|
||||
for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNextp();
|
||||
for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) {
|
||||
if (!vtxp->as<OrderMoveVertex>()->logicp()) {
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(moveGraphp.get()), vtxp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,8 +176,8 @@ void removeNonCyclic(Graph* graphp) {
|
|||
};
|
||||
|
||||
// Start with vertices with no inputs or outputs
|
||||
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
if (vtxp->inEmpty() || vtxp->outEmpty()) enqueue(vtxp);
|
||||
for (V3GraphVertex& vtx : graphp->vertices()) {
|
||||
if (vtx.inEmpty() || vtx.outEmpty()) enqueue(&vtx);
|
||||
}
|
||||
|
||||
// Iterate while we still have candidates
|
||||
|
|
@ -189,16 +189,14 @@ void removeNonCyclic(Graph* graphp) {
|
|||
|
||||
if (vtxp->inEmpty()) {
|
||||
// Enqueue children for consideration, remove out edges, and delete this vertex
|
||||
for (V3GraphEdge *edgep = vtxp->outBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp();
|
||||
for (V3GraphEdge* const edgep : vtxp->outEdges().unlinkable()) {
|
||||
enqueue(edgep->top());
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
}
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(graphp), vtxp);
|
||||
} else if (vtxp->outEmpty()) {
|
||||
// Enqueue parents for consideration, remove in edges, and delete this vertex
|
||||
for (V3GraphEdge *edgep = vtxp->inBeginp(), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->inNextp();
|
||||
for (V3GraphEdge* const edgep : vtxp->inEdges().unlinkable()) {
|
||||
enqueue(edgep->fromp());
|
||||
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
}
|
||||
|
|
@ -209,11 +207,11 @@ void removeNonCyclic(Graph* graphp) {
|
|||
|
||||
// Has this VarVertex been cut? (any edges in or out has been cut)
|
||||
bool isCut(const SchedAcyclicVarVertex* vtxp) {
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
if (edgep->weight() == 0) return true;
|
||||
for (const V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
if (edge.weight() == 0) return true;
|
||||
}
|
||||
for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->weight() == 0) return true;
|
||||
for (const V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
if (edge.weight() == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -221,8 +219,8 @@ bool isCut(const SchedAcyclicVarVertex* vtxp) {
|
|||
std::vector<SchedAcyclicVarVertex*> findCutVertices(Graph* graphp) {
|
||||
std::vector<SchedAcyclicVarVertex*> result;
|
||||
const VNUser1InUse user1InUse; // bool: already added to result
|
||||
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
if (SchedAcyclicVarVertex* const vvtxp = vtxp->cast<SchedAcyclicVarVertex>()) {
|
||||
for (V3GraphVertex& vtx : graphp->vertices()) {
|
||||
if (SchedAcyclicVarVertex* const vvtxp = vtx.cast<SchedAcyclicVarVertex>()) {
|
||||
if (!vvtxp->vscp()->user1SetOnce() && isCut(vvtxp)) result.push_back(vvtxp);
|
||||
}
|
||||
}
|
||||
|
|
@ -231,8 +229,8 @@ std::vector<SchedAcyclicVarVertex*> findCutVertices(Graph* graphp) {
|
|||
|
||||
void resetEdgeWeights(const std::vector<SchedAcyclicVarVertex*>& cutVertices) {
|
||||
for (SchedAcyclicVarVertex* const vvtxp : cutVertices) {
|
||||
for (V3GraphEdge* ep = vvtxp->inBeginp(); ep; ep = ep->inNextp()) ep->weight(1);
|
||||
for (V3GraphEdge* ep = vvtxp->outBeginp(); ep; ep = ep->outNextp()) ep->weight(1);
|
||||
for (V3GraphEdge& e : vvtxp->inEdges()) e.weight(1);
|
||||
for (V3GraphEdge& e : vvtxp->outEdges()) e.weight(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,19 +250,18 @@ void gatherSCCCandidates(V3GraphVertex* vtxp, std::vector<Candidate>& candidates
|
|||
&& name.find("__Vdly") == string::npos // Ignore internal signals
|
||||
&& name.find("__Vcell") == string::npos) {
|
||||
// Also compute the fanout of this vertex
|
||||
unsigned fanout = 0;
|
||||
for (V3GraphEdge* ep = vtxp->outBeginp(); ep; ep = ep->outNextp()) ++fanout;
|
||||
const unsigned fanout = vtxp->outEdges().size();
|
||||
candidates.emplace_back(vvtxp, fanout);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through all the vertices within the same strongly connected component (same color)
|
||||
for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
V3GraphVertex* const top = edgep->top();
|
||||
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
V3GraphVertex* const top = edge.top();
|
||||
if (top->color() == vtxp->color()) gatherSCCCandidates(top, candidates);
|
||||
}
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
V3GraphVertex* const fromp = edgep->fromp();
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
V3GraphVertex* const fromp = edge.fromp();
|
||||
if (fromp->color() == vtxp->color()) gatherSCCCandidates(fromp, candidates);
|
||||
}
|
||||
}
|
||||
|
|
@ -359,9 +356,9 @@ LogicByScope fixCuts(AstNetlist* netlistp,
|
|||
{
|
||||
const VNUser1InUse user1InUse; // bool: already added to 'lvtxps'
|
||||
for (SchedAcyclicVarVertex* const vvtxp : cutVertices) {
|
||||
for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
for (V3GraphEdge& edge : vvtxp->outEdges()) {
|
||||
SchedAcyclicLogicVertex* const lvtxp
|
||||
= static_cast<SchedAcyclicLogicVertex*>(edgep->top());
|
||||
= static_cast<SchedAcyclicLogicVertex*>(edge.top());
|
||||
if (!lvtxp->logicp()->user1SetOnce()) lvtxps.push_back(lvtxp);
|
||||
lvtx2Cuts[lvtxp].push_back(vvtxp->vscp());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,13 +290,13 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void colorActiveRegion(const V3Graph& graph) {
|
||||
void colorActiveRegion(V3Graph& graph) {
|
||||
// Work queue for depth first traversal
|
||||
std::vector<V3GraphVertex*> queue{};
|
||||
|
||||
// Trace from all SchedSenVertex
|
||||
for (V3GraphVertex* vtxp = graph.verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
if (const auto activeEventVtxp = vtxp->cast<SchedSenVertex>()) {
|
||||
for (V3GraphVertex& vtx : graph.vertices()) {
|
||||
if (const auto activeEventVtxp = vtx.cast<SchedSenVertex>()) {
|
||||
queue.push_back(activeEventVtxp);
|
||||
}
|
||||
}
|
||||
|
|
@ -313,17 +313,15 @@ void colorActiveRegion(const V3Graph& graph) {
|
|||
vtx.color(1);
|
||||
|
||||
// Enqueue all parent vertices that feed this vertex.
|
||||
for (V3GraphEdge* edgep = vtx.inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
queue.push_back(edgep->fromp());
|
||||
}
|
||||
for (V3GraphEdge& edge : vtx.inEdges()) queue.push_back(edge.fromp());
|
||||
|
||||
// If this is a logic vertex, also enqueue all variable vertices that are driven from this
|
||||
// logic. This will ensure that if a variable is set in the active region, then all
|
||||
// settings of that variable will be in the active region.
|
||||
if (vtx.is<SchedLogicVertex>()) {
|
||||
for (V3GraphEdge* edgep = vtx.outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
UASSERT(edgep->top()->is<SchedVarVertex>(), "Should be var vertex");
|
||||
queue.push_back(edgep->top());
|
||||
for (V3GraphEdge& edge : vtx.outEdges()) {
|
||||
UASSERT(edge.top()->is<SchedVarVertex>(), "Should be var vertex");
|
||||
queue.push_back(edge.top());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -346,8 +344,8 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo
|
|||
|
||||
LogicRegions result;
|
||||
|
||||
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
if (const auto lvtxp = vtxp->cast<SchedLogicVertex>()) {
|
||||
for (V3GraphVertex& vtx : graphp->vertices()) {
|
||||
if (const auto lvtxp = vtx.cast<SchedLogicVertex>()) {
|
||||
LogicByScope& lbs = lvtxp->color() ? result.m_act : result.m_nba;
|
||||
AstNode* const logicp = lvtxp->logicp();
|
||||
logicp->unlinkFrBack();
|
||||
|
|
|
|||
|
|
@ -225,8 +225,8 @@ void propagateDrivingRegions(SchedReplicateVertex* vtxp) {
|
|||
|
||||
// Compute union of driving regions of all inputs
|
||||
uint8_t drivingRegions = 0;
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
SchedReplicateVertex* const srcp = edgep->fromp()->as<SchedReplicateVertex>();
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
SchedReplicateVertex* const srcp = edge.fromp()->as<SchedReplicateVertex>();
|
||||
propagateDrivingRegions(srcp);
|
||||
drivingRegions |= srcp->drivingRegions();
|
||||
}
|
||||
|
|
@ -240,8 +240,8 @@ void propagateDrivingRegions(SchedReplicateVertex* vtxp) {
|
|||
|
||||
LogicReplicas replicate(Graph* graphp) {
|
||||
LogicReplicas result;
|
||||
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
if (SchedReplicateLogicVertex* const lvtxp = vtxp->cast<SchedReplicateLogicVertex>()) {
|
||||
for (V3GraphVertex& vtx : graphp->vertices()) {
|
||||
if (SchedReplicateLogicVertex* const lvtxp = vtx.cast<SchedReplicateLogicVertex>()) {
|
||||
const auto replicateTo = [&](LogicByScope& lbs) {
|
||||
lbs.add(lvtxp->scopep(), lvtxp->senTreep(), lvtxp->logicp()->cloneTree(false));
|
||||
};
|
||||
|
|
@ -264,8 +264,8 @@ LogicReplicas replicateLogic(LogicRegions& logicRegionsRegions) {
|
|||
// Dump for debug
|
||||
if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate");
|
||||
// Propagate driving region flags
|
||||
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
|
||||
propagateDrivingRegions(vtxp->as<SchedReplicateVertex>());
|
||||
for (V3GraphVertex& vtx : graphp->vertices()) {
|
||||
propagateDrivingRegions(vtx.as<SchedReplicateVertex>());
|
||||
}
|
||||
// Dump for debug
|
||||
if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate-propagated");
|
||||
|
|
|
|||
|
|
@ -317,17 +317,16 @@ protected:
|
|||
}
|
||||
|
||||
void pruneDepsOnInputs() {
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
if (!vertexp->outBeginp() && vertexp->is<SplitVarStdVertex>()) {
|
||||
for (V3GraphVertex& vertex : m_graph.vertices()) {
|
||||
if (vertex.outEmpty() && vertex.is<SplitVarStdVertex>()) {
|
||||
if (debug() >= 9) {
|
||||
const SplitVarStdVertex* const stdp = static_cast<SplitVarStdVertex*>(vertexp);
|
||||
UINFO(0, "Will prune deps on var " << stdp->nodep() << endl);
|
||||
stdp->nodep()->dumpTree("- ");
|
||||
const SplitVarStdVertex& sVtx = static_cast<SplitVarStdVertex&>(vertex);
|
||||
UINFO(0, "Will prune deps on var " << sVtx.nodep() << endl);
|
||||
sVtx.nodep()->dumpTree("- ");
|
||||
}
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
SplitEdge* const oedgep = static_cast<SplitEdge*>(edgep);
|
||||
oedgep->setIgnoreThisStep();
|
||||
for (V3GraphEdge& edge : vertex.inEdges()) {
|
||||
SplitEdge& oedge = static_cast<SplitEdge&>(edge);
|
||||
oedge.setIgnoreThisStep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -475,19 +474,16 @@ protected:
|
|||
|
||||
// For reordering this single block only, mark all logic
|
||||
// vertexes not involved with this step as unimportant
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
if (!vertexp->user()) {
|
||||
if (const SplitLogicVertex* const vvertexp = vertexp->cast<SplitLogicVertex>()) {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep;
|
||||
edgep = edgep->inNextp()) {
|
||||
SplitEdge* const oedgep = static_cast<SplitEdge*>(edgep);
|
||||
oedgep->setIgnoreThisStep();
|
||||
for (V3GraphVertex& vertex : m_graph.vertices()) {
|
||||
if (!vertex.user()) {
|
||||
if (const SplitLogicVertex* const vvertexp = vertex.cast<SplitLogicVertex>()) {
|
||||
for (V3GraphEdge& edge : vertex.inEdges()) {
|
||||
SplitEdge& oedge = static_cast<SplitEdge&>(edge);
|
||||
oedge.setIgnoreThisStep();
|
||||
}
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep;
|
||||
edgep = edgep->outNextp()) {
|
||||
SplitEdge* const oedgep = static_cast<SplitEdge*>(edgep);
|
||||
oedgep->setIgnoreThisStep();
|
||||
for (V3GraphEdge& edge : vertex.outEdges()) {
|
||||
SplitEdge& oedge = static_cast<SplitEdge&>(edge);
|
||||
oedge.setIgnoreThisStep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -904,18 +900,17 @@ protected:
|
|||
// For any 'if' node whose deps have all been pruned
|
||||
// (meaning, its conditional expression only looks at primary
|
||||
// inputs) prune all edges that depend on the 'if'.
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
const SplitLogicVertex* const logicp = vertexp->cast<const SplitLogicVertex>();
|
||||
for (V3GraphVertex& vertex : m_graph.vertices()) {
|
||||
SplitLogicVertex* const logicp = vertex.cast<SplitLogicVertex>();
|
||||
if (!logicp) continue;
|
||||
|
||||
const AstNodeIf* const ifNodep = VN_CAST(logicp->nodep(), NodeIf);
|
||||
if (!ifNodep) continue;
|
||||
|
||||
bool pruneMe = true;
|
||||
for (V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const SplitEdge* const oedgep = static_cast<const SplitEdge*>(edgep);
|
||||
if (!oedgep->ignoreThisStep()) {
|
||||
for (const V3GraphEdge& edge : logicp->outEdges()) {
|
||||
const SplitEdge& oedge = static_cast<const SplitEdge&>(edge);
|
||||
if (!oedge.ignoreThisStep()) {
|
||||
// This if conditional depends on something we can't
|
||||
// prune -- a variable generated in the current block.
|
||||
pruneMe = false;
|
||||
|
|
@ -923,11 +918,11 @@ protected:
|
|||
// When we can't prune dependencies on the conditional,
|
||||
// give a hint about why...
|
||||
if (debug() >= 9) {
|
||||
V3GraphVertex* vxp = oedgep->top();
|
||||
V3GraphVertex* vxp = oedge.top();
|
||||
const SplitNodeVertex* const nvxp
|
||||
= static_cast<const SplitNodeVertex*>(vxp);
|
||||
UINFO(0, "Cannot prune if-node due to edge "
|
||||
<< oedgep << " pointing to node " << nvxp->nodep() << endl);
|
||||
<< &oedge << " pointing to node " << nvxp->nodep() << endl);
|
||||
nvxp->nodep()->dumpTree("- ");
|
||||
}
|
||||
|
||||
|
|
@ -938,9 +933,9 @@ protected:
|
|||
if (!pruneMe) continue;
|
||||
|
||||
// This if can be split; prune dependencies on it.
|
||||
for (V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
SplitEdge* const oedgep = static_cast<SplitEdge*>(edgep);
|
||||
oedgep->setIgnoreThisStep();
|
||||
for (V3GraphEdge& edge : logicp->inEdges()) {
|
||||
SplitEdge& oedge = static_cast<SplitEdge&>(edge);
|
||||
oedge.setIgnoreThisStep();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,9 @@ private:
|
|||
};
|
||||
|
||||
static Vertex* castVertexp(V3GraphVertex* vxp) { return static_cast<Vertex*>(vxp); }
|
||||
static const Vertex* castVertexp(const V3GraphVertex* vxp) {
|
||||
return static_cast<const Vertex*>(vxp);
|
||||
}
|
||||
|
||||
public:
|
||||
// From *this, populate *mstp with the minimum spanning tree.
|
||||
|
|
@ -156,8 +159,8 @@ public:
|
|||
// Use Prim's algorithm to efficiently construct the MST.
|
||||
|
||||
uint32_t vertCount = 0;
|
||||
for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
mstp->addVertex(castVertexp(vxp)->key());
|
||||
for (V3GraphVertex& vtx : vertices()) {
|
||||
mstp->addVertex(castVertexp(&vtx)->key());
|
||||
vertCount++;
|
||||
}
|
||||
|
||||
|
|
@ -181,12 +184,12 @@ public:
|
|||
// Allocate new edge list
|
||||
EdgeList* const newEdgesp = &allocatedEdgeLists[vertIdx++];
|
||||
// Gather out edges of this vertex
|
||||
for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
// Don't add edges leading to vertices we already visited. This is a highly
|
||||
// connected graph, so this greatly reduces the cost of maintaining the pending
|
||||
// set.
|
||||
if (edgep->top()->user() == VertexState::MST_VISITED) continue;
|
||||
newEdgesp->push_back(edgep);
|
||||
if (edge.top()->user() == VertexState::MST_VISITED) continue;
|
||||
newEdgesp->push_back(&edge);
|
||||
}
|
||||
// If no relevant out edges, then we are done
|
||||
if (newEdgesp->empty()) return;
|
||||
|
|
@ -197,7 +200,7 @@ public:
|
|||
};
|
||||
|
||||
// To start, choose an arbitrary vertex and visit it.
|
||||
visit(verticesBeginp());
|
||||
visit(vertices().frontp());
|
||||
|
||||
// Repeatedly find the least costly edge in the pending set.
|
||||
// If it connects to an unvisited node, visit that node and update
|
||||
|
|
@ -282,14 +285,14 @@ public:
|
|||
std::vector<V3GraphEdge*> pendingEdges;
|
||||
|
||||
for (Vertex* const fromp : odds) {
|
||||
for (V3GraphEdge* edgep = fromp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
Vertex* const top = castVertexp(edgep->top());
|
||||
for (V3GraphEdge& edge : fromp->outEdges()) {
|
||||
Vertex* const top = castVertexp(edge.top());
|
||||
// There are two edges (in both directions) between these two vertices. Keep one.
|
||||
if (fromp > top) continue;
|
||||
// We only care about edges between the odd-order vertices
|
||||
if (top->user() != VertexState::UNMATCHED_ODD) continue;
|
||||
// Add to candidate list
|
||||
pendingEdges.push_back(edgep);
|
||||
pendingEdges.push_back(&edge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -313,12 +316,12 @@ public:
|
|||
|
||||
void combineGraph(const TspGraphTmpl& g) {
|
||||
std::unordered_set<uint32_t> edges_done;
|
||||
for (V3GraphVertex* vxp = g.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
const Vertex* const fromp = castVertexp(vxp);
|
||||
for (V3GraphEdge* edgep = fromp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const Vertex* const top = castVertexp(edgep->top());
|
||||
if (edges_done.insert(getEdgeId(edgep)).second) {
|
||||
addEdge(fromp->key(), top->key(), edgep->weight());
|
||||
for (const V3GraphVertex& vtx : g.vertices()) {
|
||||
const Vertex* const fromp = castVertexp(&vtx);
|
||||
for (const V3GraphEdge& edge : fromp->outEdges()) {
|
||||
const Vertex* const top = castVertexp(edge.top());
|
||||
if (edges_done.insert(getEdgeId(&edge)).second) {
|
||||
addEdge(fromp->key(), top->key(), edge.weight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -335,12 +338,12 @@ public:
|
|||
tour.push_back(cur_vertexp);
|
||||
|
||||
// Look for an arbitrary edge we've not yet marked
|
||||
for (V3GraphEdge* edgep = cur_vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const uint32_t edgeId = getEdgeId(edgep);
|
||||
for (V3GraphEdge& edge : cur_vertexp->outEdges()) {
|
||||
const uint32_t edgeId = getEdgeId(&edge);
|
||||
if (markedEdgesp->end() == markedEdgesp->find(edgeId)) {
|
||||
// This edge is not yet marked, so follow it.
|
||||
markedEdgesp->insert(edgeId);
|
||||
Vertex* const neighborp = castVertexp(edgep->top());
|
||||
Vertex* const neighborp = castVertexp(edge.top());
|
||||
UINFO(6, "following edge " << edgeId << " from " << cur_vertexp->key()
|
||||
<< " to " << neighborp->key() << endl);
|
||||
cur_vertexp = neighborp;
|
||||
|
|
@ -359,8 +362,8 @@ public:
|
|||
do {
|
||||
recursed = false;
|
||||
// Look for an arbitrary edge at vxp we've not yet marked
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const uint32_t edgeId = getEdgeId(edgep);
|
||||
for (V3GraphEdge& edge : vxp->outEdges()) {
|
||||
const uint32_t edgeId = getEdgeId(&edge);
|
||||
if (markedEdgesp->end() == markedEdgesp->find(edgeId)) {
|
||||
UINFO(6, "Recursing.\n");
|
||||
findEulerTourRecurse(markedEdgesp, vxp, sortedOutp);
|
||||
|
|
@ -381,12 +384,12 @@ public:
|
|||
void dumpGraph(std::ostream& os, const string& nameComment) const {
|
||||
// UINFO(0) as controlled by caller
|
||||
os << "At " << nameComment << ", dumping graph. Keys:\n";
|
||||
for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
const Vertex* const tspvp = castVertexp(vxp);
|
||||
for (const V3GraphVertex& vtx : vertices()) {
|
||||
const Vertex* const tspvp = castVertexp(&vtx);
|
||||
os << " " << tspvp->key() << '\n';
|
||||
for (V3GraphEdge* edgep = tspvp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
const Vertex* const neighborp = castVertexp(edgep->top());
|
||||
os << " has edge " << getEdgeId(edgep) << " to " << neighborp->key() << '\n';
|
||||
for (const V3GraphEdge& edge : tspvp->outEdges()) {
|
||||
const Vertex* const neighborp = castVertexp(edge.top());
|
||||
os << " has edge " << getEdgeId(&edge) << " to " << neighborp->key() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -404,18 +407,15 @@ public:
|
|||
if (::dumpGraphLevel() >= 6) dumpDotFilePrefixed("findEulerTour");
|
||||
std::unordered_set<unsigned /*edgeID*/> markedEdges;
|
||||
// Pick a start node
|
||||
Vertex* const start_vertexp = castVertexp(verticesBeginp());
|
||||
Vertex* const start_vertexp = castVertexp(vertices().frontp());
|
||||
findEulerTourRecurse(&markedEdges, start_vertexp, sortedOutp);
|
||||
}
|
||||
|
||||
std::vector<T_Key> getOddDegreeKeys() const {
|
||||
std::vector<T_Key> result;
|
||||
for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
const Vertex* const tspvp = castVertexp(vxp);
|
||||
uint32_t degree = 0;
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
degree++;
|
||||
}
|
||||
for (const V3GraphVertex& vtx : vertices()) {
|
||||
const Vertex* const tspvp = castVertexp(&vtx);
|
||||
const uint32_t degree = vtx.outEdges().size();
|
||||
if (degree & 1) result.push_back(tspvp->key());
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -153,8 +153,8 @@ private:
|
|||
<< vxp->impureNode()->warnContextSecondary());
|
||||
}
|
||||
// And, we need to check all tasks this task calls
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
checkPurity(nodep, static_cast<TaskBaseVertex*>(edgep->top()));
|
||||
for (V3GraphEdge& edge : vxp->outEdges()) {
|
||||
checkPurity(nodep, static_cast<TaskBaseVertex*>(edge.top()));
|
||||
}
|
||||
}
|
||||
TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) {
|
||||
|
|
|
|||
|
|
@ -227,8 +227,8 @@ class TimingSuspendableVisitor final : public VNVisitor {
|
|||
// Propagate flag to all nodes that depend on the given one
|
||||
void propagateFlags(DepVtx* const vxp, NodeFlag flag) {
|
||||
auto* const parentp = vxp->nodep();
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edgep->top());
|
||||
for (V3GraphEdge& edge : vxp->outEdges()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edge.top());
|
||||
AstNode* const depp = depVxp->nodep();
|
||||
if (passFlag(parentp, depp, flag)) propagateFlags(depVxp, flag);
|
||||
}
|
||||
|
|
@ -236,19 +236,19 @@ class TimingSuspendableVisitor final : public VNVisitor {
|
|||
template <typename Predicate>
|
||||
void propagateFlagsIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
|
||||
auto* const parentp = vxp->nodep();
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edgep->top());
|
||||
for (V3GraphEdge& edge : vxp->outEdges()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edge.top());
|
||||
AstNode* const depp = depVxp->nodep();
|
||||
if (p(edgep) && passFlag(parentp, depp, flag)) propagateFlagsIf(depVxp, flag, p);
|
||||
if (p(&edge) && passFlag(parentp, depp, flag)) propagateFlagsIf(depVxp, flag, p);
|
||||
}
|
||||
}
|
||||
template <typename Predicate>
|
||||
void propagateFlagsReversedIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
|
||||
auto* const parentp = vxp->nodep();
|
||||
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edgep->fromp());
|
||||
for (V3GraphEdge& edge : vxp->inEdges()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edge.fromp());
|
||||
AstNode* const depp = depVxp->nodep();
|
||||
if (p(edgep) && passFlag(parentp, depp, flag))
|
||||
if (p(&edge) && passFlag(parentp, depp, flag))
|
||||
propagateFlagsReversedIf(depVxp, flag, p);
|
||||
}
|
||||
}
|
||||
|
|
@ -408,34 +408,34 @@ public:
|
|||
m_suspGraph.removeTransitiveEdges();
|
||||
m_procGraph.removeTransitiveEdges();
|
||||
// Propagate suspendability
|
||||
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||
if (hasFlags(depVxp->nodep(), T_SUSPENDEE)) propagateFlags(depVxp, T_SUSPENDEE);
|
||||
for (V3GraphVertex& vtx : m_suspGraph.vertices()) {
|
||||
DepVtx& depVtx = static_cast<DepVtx&>(vtx);
|
||||
if (hasFlags(depVtx.nodep(), T_SUSPENDEE)) propagateFlags(&depVtx, T_SUSPENDEE);
|
||||
}
|
||||
if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
|
||||
|
||||
// Propagate T_HAS_PROCESS
|
||||
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||
for (V3GraphVertex& vtx : m_procGraph.vertices()) {
|
||||
DepVtx& depVtx = static_cast<DepVtx&>(vtx);
|
||||
// Find processes that'll allocate VlProcess
|
||||
if (hasFlags(depVxp->nodep(), T_FORCES_PROC)) {
|
||||
propagateFlagsIf(depVxp, T_FORCES_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
if (hasFlags(depVtx.nodep(), T_FORCES_PROC)) {
|
||||
propagateFlagsIf(&depVtx, T_FORCES_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
return !hasFlags(static_cast<DepVtx*>(e->fromp())->nodep(), T_ALLOCS_PROC);
|
||||
});
|
||||
}
|
||||
// Mark nodes on paths between processes and statements that use VlProcess
|
||||
if (hasFlags(depVxp->nodep(), T_NEEDS_PROC)) {
|
||||
propagateFlagsIf(depVxp, T_NEEDS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
if (hasFlags(depVtx.nodep(), T_NEEDS_PROC)) {
|
||||
propagateFlagsIf(&depVtx, T_NEEDS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
return !hasFlags(static_cast<DepVtx*>(e->top())->nodep(), T_ALLOCS_PROC);
|
||||
});
|
||||
}
|
||||
}
|
||||
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||
for (V3GraphVertex& vtx : m_procGraph.vertices()) {
|
||||
DepVtx& depVtx = static_cast<DepVtx&>(vtx);
|
||||
// Mark nodes that will be emitted with a VlProcess argument
|
||||
if (hasFlags(depVxp->nodep(), T_ALLOCS_PROC | T_FORCES_PROC)) {
|
||||
addFlags(depVxp->nodep(), T_HAS_PROC);
|
||||
propagateFlagsReversedIf(depVxp, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
if (hasFlags(depVtx.nodep(), T_ALLOCS_PROC | T_FORCES_PROC)) {
|
||||
addFlags(depVtx.nodep(), T_HAS_PROC);
|
||||
propagateFlagsReversedIf(&depVtx, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
return hasFlags(static_cast<DepVtx*>(e->fromp())->nodep(), T_NEEDS_PROC);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,8 +202,8 @@ class TraceVisitor final : public VNVisitor {
|
|||
// Note uses user4
|
||||
V3DupFinder dupFinder; // Duplicate code detection
|
||||
// Hash all of the traced values and find if there are any duplicates
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (TraceTraceVertex* const vvertexp = itp->cast<TraceTraceVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (TraceTraceVertex* const vvertexp = vtx.cast<TraceTraceVertex>()) {
|
||||
const AstTraceDecl* const nodep = vvertexp->nodep();
|
||||
UASSERT_OBJ(!vvertexp->duplicatep(), nodep, "Should not be a duplicate");
|
||||
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
|
||||
|
|
@ -227,9 +227,8 @@ class TraceVisitor final : public VNVisitor {
|
|||
void graphSimplify(bool initial) {
|
||||
if (initial) {
|
||||
// Remove all variable nodes
|
||||
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
|
||||
nextp = itp->verticesNextp();
|
||||
if (TraceVarVertex* const vvertexp = itp->cast<TraceVarVertex>()) {
|
||||
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
||||
if (TraceVarVertex* const vvertexp = vtxp->cast<TraceVarVertex>()) {
|
||||
vvertexp->rerouteEdges(&m_graph);
|
||||
vvertexp->unlinkDelete(&m_graph);
|
||||
}
|
||||
|
|
@ -239,9 +238,8 @@ class TraceVisitor final : public VNVisitor {
|
|||
// expansion.
|
||||
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
||||
// Remove all Cfunc nodes
|
||||
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
|
||||
nextp = itp->verticesNextp();
|
||||
if (TraceCFuncVertex* const vvertexp = itp->cast<TraceCFuncVertex>()) {
|
||||
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
||||
if (TraceCFuncVertex* const vvertexp = vtxp->cast<TraceCFuncVertex>()) {
|
||||
vvertexp->rerouteEdges(&m_graph);
|
||||
vvertexp->unlinkDelete(&m_graph);
|
||||
}
|
||||
|
|
@ -252,44 +250,42 @@ class TraceVisitor final : public VNVisitor {
|
|||
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
||||
|
||||
// If there are any edges from a always, keep only the always
|
||||
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
||||
itp = itp->verticesNextp()) {
|
||||
if (const TraceTraceVertex* const vvertexp = itp->cast<const TraceTraceVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (TraceTraceVertex* const vvertexp = vtx.cast<TraceTraceVertex>()) {
|
||||
// Search for the incoming always edge
|
||||
const V3GraphEdge* alwaysEdgep = nullptr;
|
||||
for (const V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;
|
||||
edgep = edgep->inNextp()) {
|
||||
for (const V3GraphEdge& edge : vvertexp->inEdges()) {
|
||||
const TraceActivityVertex* const actVtxp
|
||||
= edgep->fromp()->as<const TraceActivityVertex>();
|
||||
= edge.fromp()->as<const TraceActivityVertex>();
|
||||
if (actVtxp->activityAlways()) {
|
||||
alwaysEdgep = edgep;
|
||||
alwaysEdgep = &edge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If always edge exists, remove all other edges
|
||||
if (alwaysEdgep) {
|
||||
for (V3GraphEdge *nextp, *edgep = vvertexp->inBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->inNextp();
|
||||
if (edgep != alwaysEdgep) edgep->unlinkDelete();
|
||||
for (V3GraphEdge* const edgep : vvertexp->inEdges().unlinkable()) {
|
||||
if (edgep != alwaysEdgep) VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Activity points with no outputs can be removed
|
||||
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
|
||||
nextp = itp->verticesNextp();
|
||||
if (TraceActivityVertex* const vtxp = itp->cast<TraceActivityVertex>()) {
|
||||
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
||||
if (TraceActivityVertex* const aVtxp = vtxp->cast<TraceActivityVertex>()) {
|
||||
// Leave in the always vertex for later use.
|
||||
if (vtxp != m_alwaysVtxp && !vtxp->outBeginp()) vtxp->unlinkDelete(&m_graph);
|
||||
if (aVtxp != m_alwaysVtxp && aVtxp->outEmpty()) {
|
||||
VL_DO_DANGLING(aVtxp->unlinkDelete(&m_graph), aVtxp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t assignactivityNumbers() {
|
||||
uint32_t activityNumber = 1; // Note 0 indicates "slow" only
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (TraceActivityVertex* const vvertexp = itp->cast<TraceActivityVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (TraceActivityVertex* const vvertexp = vtx.cast<TraceActivityVertex>()) {
|
||||
if (vvertexp != m_alwaysVtxp) {
|
||||
if (vvertexp->slow()) {
|
||||
vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW);
|
||||
|
|
@ -306,15 +302,14 @@ class TraceVisitor final : public VNVisitor {
|
|||
// Populate sort structure
|
||||
traces.clear();
|
||||
nNonConstCodes = 0;
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (TraceTraceVertex* const vtxp = itp->cast<TraceTraceVertex>()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (TraceTraceVertex* const vtxp = vtx.cast<TraceTraceVertex>()) {
|
||||
ActCodeSet actSet;
|
||||
UINFO(9, " Add to sort: " << vtxp << endl);
|
||||
if (debug() >= 9) vtxp->nodep()->dumpTree("- trnode: ");
|
||||
for (const V3GraphEdge* edgep = vtxp->inBeginp(); edgep;
|
||||
edgep = edgep->inNextp()) {
|
||||
for (const V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
const TraceActivityVertex* const cfvertexp
|
||||
= edgep->fromp()->cast<const TraceActivityVertex>();
|
||||
= edge.fromp()->cast<const TraceActivityVertex>();
|
||||
UASSERT_OBJ(cfvertexp, vtxp->nodep(),
|
||||
"Should have been function pointing to this trace");
|
||||
UINFO(9, " Activity: " << cfvertexp << endl);
|
||||
|
|
@ -444,9 +439,8 @@ class TraceVisitor final : public VNVisitor {
|
|||
m_activityVscp = newvscp;
|
||||
|
||||
// Insert activity setters
|
||||
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
||||
itp = itp->verticesNextp()) {
|
||||
if (const TraceActivityVertex* const vtxp = itp->cast<const TraceActivityVertex>()) {
|
||||
for (const V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (const TraceActivityVertex* const vtxp = vtx.cast<const TraceActivityVertex>()) {
|
||||
if (vtxp->activitySlow()) {
|
||||
// Just set all flags in slow code as it should be rare.
|
||||
// This will be rolled up into a loop by V3Reloop.
|
||||
|
|
|
|||
|
|
@ -217,8 +217,8 @@ private:
|
|||
vtxp->user(1); // Recursed
|
||||
UINFO(9, " Mark tri " << level << " " << vtxp << endl);
|
||||
if (!vtxp->varp()) { // not a var where we stop the recursion
|
||||
for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edgep->top());
|
||||
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edge.top());
|
||||
// Doesn't hurt to not check if already set, but by doing so when we
|
||||
// print out the debug messages, we'll see this node at level 0 instead.
|
||||
if (!vvertexp->isTristate()) {
|
||||
|
|
@ -229,8 +229,8 @@ private:
|
|||
} else {
|
||||
// A variable is tristated. Find all of the LHS VARREFs that
|
||||
// drive this signal now need tristate drivers
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edgep->fromp());
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edge.fromp());
|
||||
if (const AstVarRef* const refp = VN_CAST(vvertexp->nodep(), VarRef)) {
|
||||
if (refp->access().isWriteOrRW()
|
||||
// Doesn't hurt to not check if already set, but by doing so when we
|
||||
|
|
@ -254,8 +254,8 @@ private:
|
|||
vtxp->user(3); // Recursed
|
||||
UINFO(9, " Mark feedstri " << level << " " << vtxp << endl);
|
||||
if (!vtxp->varp()) { // not a var where we stop the recursion
|
||||
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edgep->fromp());
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
TristateVertex* const vvertexp = static_cast<TristateVertex*>(edge.fromp());
|
||||
// Doesn't hurt to not check if already set, but by doing so when we
|
||||
// print out the debug messages, we'll see this node at level 0 instead.
|
||||
if (!vvertexp->feedsTri()) {
|
||||
|
|
@ -270,13 +270,13 @@ public:
|
|||
// METHODS
|
||||
bool empty() const { return m_graph.empty(); }
|
||||
void clear() {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
const TristateVertex* const vvertexp = static_cast<TristateVertex*>(itp);
|
||||
if (vvertexp->isTristate() && !vvertexp->processed()) {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
const TristateVertex& vvertex = static_cast<TristateVertex&>(vtx);
|
||||
if (vvertex.isTristate() && !vvertex.processed()) {
|
||||
// Not v3errorSrc as no reason to stop the world
|
||||
vvertexp->nodep()->v3error("Unsupported tristate construct"
|
||||
" (in graph; not converted): "
|
||||
<< vvertexp->nodep()->prettyTypeName());
|
||||
vvertex.nodep()->v3error("Unsupported tristate construct"
|
||||
" (in graph; not converted): "
|
||||
<< vvertex.nodep()->prettyTypeName());
|
||||
}
|
||||
}
|
||||
m_graph.clear();
|
||||
|
|
@ -284,11 +284,11 @@ public:
|
|||
}
|
||||
void graphWalk(AstNodeModule* nodep) {
|
||||
UINFO(9, " Walking " << nodep << endl);
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
graphWalkRecurseFwd(static_cast<TristateVertex*>(itp), 0);
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
graphWalkRecurseFwd(static_cast<TristateVertex*>(&vtx), 0);
|
||||
}
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
graphWalkRecurseBack(static_cast<TristateVertex*>(itp), 0);
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
graphWalkRecurseBack(static_cast<TristateVertex*>(&vtx), 0);
|
||||
}
|
||||
if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name());
|
||||
}
|
||||
|
|
@ -332,10 +332,10 @@ public:
|
|||
VarVec tristateVars() {
|
||||
// Return all tristate variables
|
||||
VarVec v;
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
const TristateVertex* const vvertexp = static_cast<TristateVertex*>(itp);
|
||||
if (vvertexp->isTristate()) {
|
||||
if (AstVar* const nodep = VN_CAST(vvertexp->nodep(), Var)) v.push_back(nodep);
|
||||
for (const V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
const TristateVertex& vvertex = static_cast<const TristateVertex&>(vtx);
|
||||
if (vvertex.isTristate()) {
|
||||
if (AstVar* const nodep = VN_CAST(vvertex.nodep(), Var)) v.push_back(nodep);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
|
|
|
|||
|
|
@ -275,9 +275,8 @@ void V3VariableOrder::orderAll(AstNetlist* netlistp) {
|
|||
// Gather MTask affinities
|
||||
if (v3Global.opt.mtasks()) {
|
||||
netlistp->topModulep()->foreach([&](AstExecGraph* execGraphp) {
|
||||
for (const V3GraphVertex* vtxp = execGraphp->depGraphp()->verticesBeginp(); vtxp;
|
||||
vtxp = vtxp->verticesNextp()) {
|
||||
GatherMTaskAffinity::apply(vtxp->as<const ExecMTask>(), mTaskAffinity);
|
||||
for (const V3GraphVertex& vtx : execGraphp->depGraphp()->vertices()) {
|
||||
GatherMTaskAffinity::apply(vtx.as<const ExecMTask>(), mTaskAffinity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue