Improve V3List user interface (#4996)

This commit is contained in:
Geza Lore 2024-03-25 23:06:25 +00:00 committed by GitHub
parent 4df9e2e0e5
commit 98206a4f04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 2177 additions and 2158 deletions

View File

@ -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,

View File

@ -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) + '"'; }

View File

@ -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

View File

@ -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)) {

View File

@ -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>());
}
}

View File

@ -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) {

View File

@ -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.

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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; }

View File

@ -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");
}
});
}

View File

@ -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");

View File

@ -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);
}
}
}

View File

@ -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";
}

View File

@ -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

View File

@ -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
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -259,7 +259,7 @@ public:
gp->order();
// Test debug function
gp->dumpEdges(std::cout, ap);
gp->dumpEdges(std::cout, *ap);
dumpSelf();
}

View File

@ -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());

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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());
}

View File

@ -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();

View File

@ -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");

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);
});
}

View File

@ -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.

View File

@ -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;

View File

@ -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);
}
});
}