Internals: Use runtime type info instead of dynamic_cast for faster graph type checks (#4397)

This commit is contained in:
Krzysztof Bieganski 2023-09-01 00:00:53 +02:00 committed by GitHub
parent 96ee81fa3f
commit ffbbd438ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 390 additions and 143 deletions

View File

@ -47,6 +47,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Extend V3GraphVertex class for use in latch detection graph
class LatchDetectGraphVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
public:
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };

View File

@ -65,6 +65,7 @@ public:
// Support classes
class GateEitherVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(GateEitherVertex, V3GraphVertex)
AstScope* const m_scopep; // Scope vertex refers to
bool m_reducible = true; // True if this node should be able to be eliminated
bool m_dedupable = true; // True if this node should be able to be deduped
@ -122,6 +123,7 @@ public:
};
class GateVarVertex final : public GateEitherVertex {
VL_RTTI_IMPL(GateVarVertex, GateEitherVertex)
AstVarScope* const m_varScp;
bool m_isTop = false;
bool m_isClock = false;
@ -164,6 +166,7 @@ public:
};
class GateLogicVertex final : public GateEitherVertex {
VL_RTTI_IMPL(GateLogicVertex, GateEitherVertex)
AstNode* const m_nodep;
AstActive* const m_activep; // Under what active; nullptr is ok (under cfunc or such)
const bool m_slow; // In slow block
@ -568,7 +571,7 @@ public:
void GateVisitor::optimizeSignals(bool allowMultiIn) {
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp);
GateVarVertex* const vvertexp = itp->cast<GateVarVertex>();
// Consider "inlining" variables
if (!vvertexp) continue;
@ -710,7 +713,7 @@ void GateVisitor::consumedMove() {
// We need the "usually" block logic to do a better job at this
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) {
if (const GateVarVertex* const vvertexp = vertexp->cast<GateVarVertex>()) {
if (!vvertexp->consumed() && !vvertexp->user()) {
UINFO(8, "Unconsumed " << vvertexp->varScp() << endl);
}
@ -734,7 +737,7 @@ void GateVisitor::consumedMove() {
void GateVisitor::warnSignals() {
AstNode::user2ClearTree();
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (const GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
const AstVarScope* const vscp = vvertexp->varScp();
const AstNode* const sp = vvertexp->rstSyncNodep();
const AstNode* const ap = vvertexp->rstAsyncNodep();
@ -1136,14 +1139,14 @@ void GateVisitor::dedupe() {
// Traverse starting from each of the clocks
UINFO(9, "Gate dedupe() clocks:\n");
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
if (vvertexp->isClock()) deduper.dedupeTree(vvertexp);
}
}
// Traverse starting from each of the outputs
UINFO(9, "Gate dedupe() outputs:\n");
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isWritable()) {
deduper.dedupeTree(vvertexp);
}
@ -1187,8 +1190,7 @@ private:
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;) {
V3GraphEdge* oldedgep = edgep;
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
if (GateLogicVertex* const lvertexp
= dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
if (GateLogicVertex* const lvertexp = oldedgep->fromp()->cast<GateLogicVertex>()) {
if (AstNodeAssign* const assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) {
// if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) {
if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) {
@ -1274,7 +1276,7 @@ void GateVisitor::mergeAssigns() {
UINFO(6, "mergeAssigns\n");
GateMergeAssignsGraphVisitor merger{&m_graph};
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
merger.mergeAssignsTree(vvertexp);
}
}
@ -1460,7 +1462,7 @@ void GateVisitor::decomposeClkVectors() {
AstNode::user2ClearTree();
GateClkDecompGraphVisitor decomposer{&m_graph};
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vertp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vertp = itp->cast<GateVarVertex>()) {
const AstVarScope* const vsp = vertp->varScp();
if (vsp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) {
if (vsp->varp()->width() > 1) {

View File

@ -22,6 +22,7 @@
#include "V3Error.h"
#include "V3List.h"
#include "V3Rtti.h"
#include <algorithm>
@ -173,6 +174,7 @@ 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;
@ -209,6 +211,40 @@ public:
void unlinkEdges(V3Graph* graphp);
void unlinkDelete(V3Graph* graphp);
// 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"; }
@ -262,6 +298,7 @@ std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp);
//============================================================================
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
@ -308,6 +345,39 @@ public:
}
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"; }

View File

@ -32,6 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Break the minimal number of backward edges to make the graph acyclic
class GraphAcycVertex final : public V3GraphVertex {
VL_RTTI_IMPL(GraphAcycVertex, V3GraphVertex)
// user() is used for various sub-algorithm pieces
V3GraphVertex* const m_origVertexp; // Pointer to first vertex this represents
protected:
@ -56,6 +57,7 @@ public:
//--------------------------------------------------------------------
class GraphAcycEdge final : public V3GraphEdge {
VL_RTTI_IMPL(GraphAcycEdge, V3GraphEdge)
// userp() is always used to point to the head original graph edge
private:
using OrigEdgeList = std::list<V3GraphEdge*>; // List of orig edges, see also GraphAcyc's decl

View File

@ -52,6 +52,7 @@ public:
// Vertices and nodes
class V3GraphTestVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(V3GraphTestVertex, V3GraphVertex)
const string m_name;
public:

View File

@ -333,7 +333,7 @@ private:
}
for (V3GraphVertex* mtaskVxp = nodep->depGraphp()->verticesBeginp(); mtaskVxp;
mtaskVxp = mtaskVxp->verticesNextp()) {
const ExecMTask* const mtaskp = dynamic_cast<ExecMTask*>(mtaskVxp);
const ExecMTask* const mtaskp = mtaskVxp->as<ExecMTask>();
m_execMTaskp = mtaskp;
m_sequence = 0;
iterate(mtaskp->bodyp());

View File

@ -52,6 +52,7 @@ public:
};
class LinkCellsVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LinkCellsVertex, V3GraphVertex)
AstNodeModule* const m_modp;
public:
@ -69,6 +70,7 @@ public:
};
class LibraryVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LibraryVertex, V3GraphVertex)
public:
explicit LibraryVertex(V3Graph* graphp)
: V3GraphVertex{graphp} {}
@ -77,7 +79,7 @@ public:
};
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
if (const LinkCellsVertex* const vvertexp = vertexp->cast<LinkCellsVertex>()) {
vvertexp->modp()->v3warn(E_UNSUPPORTED,
"Unsupported: Recursive multiple modules (module instantiates "
"something leading back to itself): "
@ -171,7 +173,7 @@ private:
if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells");
m_graph.rank();
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(itp)) {
if (const LinkCellsVertex* const vvertexp = itp->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

@ -597,7 +597,7 @@ public:
// For each logic vertex, make a T_MoveVertex, for each variable vertex, allocate storage
for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (OrderLogicVertex* const lvtxp = dynamic_cast<OrderLogicVertex*>(itp)) {
if (OrderLogicVertex* const lvtxp = itp->cast<OrderLogicVertex>()) {
lvtxp->userp(m_vxMakerp->makeVertexp(lvtxp, nullptr, lvtxp->domainp()));
} else {
// This is an OrderVarVertex
@ -607,7 +607,7 @@ public:
}
// Build edges between logic vertices
for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (OrderLogicVertex* const lvtxp = dynamic_cast<OrderLogicVertex*>(itp)) {
if (OrderLogicVertex* const lvtxp = itp->cast<OrderLogicVertex>()) {
iterateLogicVertex(lvtxp);
}
}
@ -941,8 +941,8 @@ void OrderMoveDomScope::movedVertex(OrderProcess* opp, OrderMoveVertex* vertexp)
void OrderProcess::processDomains() {
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
OrderEitherVertex* const vertexp = dynamic_cast<OrderEitherVertex*>(itp);
UASSERT(vertexp, "Null or vertex not derived from EitherVertex");
UASSERT(itp, "Vertex should not be null");
OrderEitherVertex* const vertexp = itp->as<OrderEitherVertex>();
processDomainsIterate(vertexp);
}
}
@ -958,7 +958,7 @@ void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) {
UINFO(5, " pdi: " << vertexp << endl);
AstSenTree* domainp = nullptr;
if (OrderLogicVertex* const lvtxp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
if (OrderLogicVertex* const lvtxp = vertexp->cast<OrderLogicVertex>()) {
domainp = lvtxp->hybridp();
}
@ -970,7 +970,7 @@ void OrderProcess::processDomainsIterate(OrderEitherVertex* vertexp) {
AstSenTree* fromDomainp = fromVertexp->domainp();
UASSERT(!fromDomainp->hasCombo(), "There should be no need for combinational domains");
if (OrderVarVertex* const varVtxp = dynamic_cast<OrderVarVertex*>(fromVertexp)) {
if (OrderVarVertex* const varVtxp = fromVertexp->cast<OrderVarVertex>()) {
AstVarScope* const vscp = varVtxp->vscp();
// Add in any external domains
externalDomainps.clear();
@ -1023,13 +1023,13 @@ void OrderProcess::processEdgeReport() {
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 = dynamic_cast<OrderVarVertex*>(itp)) {
if (OrderVarVertex* const vvertexp = itp->cast<OrderVarVertex>()) {
string name(vvertexp->vscp()->prettyName());
if (dynamic_cast<OrderVarPreVertex*>(itp)) {
if (itp->is<OrderVarPreVertex>()) {
name += " {PRE}";
} else if (dynamic_cast<OrderVarPostVertex*>(itp)) {
} else if (itp->is<OrderVarPostVertex>()) {
name += " {POST}";
} else if (dynamic_cast<OrderVarPordVertex*>(itp)) {
} else if (itp->is<OrderVarPordVertex>()) {
name += " {PORD}";
}
std::ostringstream os;
@ -1335,8 +1335,7 @@ void OrderProcess::processMTasks() {
// information in V3EmitC when we lay out var's in memory.
const OrderLogicVertex* const logicp = movep->logicp();
for (const V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) {
const OrderVarVertex* const pre_varp
= dynamic_cast<const OrderVarVertex*>(edgep->fromp());
const OrderVarVertex* const pre_varp = edgep->fromp()->cast<const OrderVarVertex>();
if (!pre_varp) continue;
AstVar* const varp = pre_varp->vscp()->varp();
// varp depends on logicp, so logicp produces varp,
@ -1344,8 +1343,7 @@ void OrderProcess::processMTasks() {
varp->addProducingMTaskId(mtaskId);
}
for (const V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) {
const OrderVarVertex* const post_varp
= dynamic_cast<const OrderVarVertex*>(edgep->top());
const OrderVarVertex* const post_varp = edgep->top()->cast<const OrderVarVertex>();
if (!post_varp) continue;
AstVar* const varp = post_varp->vscp()->varp();
varp->addConsumingMTaskId(mtaskId);

View File

@ -102,6 +102,7 @@ public:
// Vertex types
class OrderEitherVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(OrderEitherVertex, V3GraphVertex)
// Event domain of vertex. For OrderLogicVertex this represents the conditions when the logic
// block must be executed. For OrderVarVertex, this is the union of the domains of all the
// OrderLogicVertex vertices that drive the variable. If initially set to nullptr (e.g.: all
@ -133,6 +134,7 @@ public:
};
class OrderLogicVertex final : public OrderEitherVertex {
VL_RTTI_IMPL(OrderLogicVertex, OrderEitherVertex)
AstNode* const m_nodep; // The logic this vertex represents
AstScope* const m_scopep; // Scope the logic is under
AstSenTree* const m_hybridp; // Additional sensitivities for hybrid combinational logic
@ -167,6 +169,7 @@ public:
};
class OrderVarVertex VL_NOT_FINAL : public OrderEitherVertex {
VL_RTTI_IMPL(OrderVarVertex, OrderEitherVertex)
AstVarScope* const m_vscp;
public:
@ -189,6 +192,7 @@ public:
};
class OrderVarStdVertex final : public OrderVarVertex {
VL_RTTI_IMPL(OrderVarStdVertex, OrderVarVertex)
public:
// CONSTRUCTOR
OrderVarStdVertex(OrderGraph* graphp, AstVarScope* vscp)
@ -205,6 +209,7 @@ public:
};
class OrderVarPreVertex final : public OrderVarVertex {
VL_RTTI_IMPL(OrderVarPreVertex, OrderVarVertex)
public:
// CONSTRUCTOR
OrderVarPreVertex(OrderGraph* graphp, AstVarScope* vscp)
@ -221,6 +226,7 @@ public:
};
class OrderVarPostVertex final : public OrderVarVertex {
VL_RTTI_IMPL(OrderVarPostVertex, OrderVarVertex)
public:
// CONSTRUCTOR
OrderVarPostVertex(OrderGraph* graphp, AstVarScope* vscp)
@ -237,6 +243,7 @@ public:
};
class OrderVarPordVertex final : public OrderVarVertex {
VL_RTTI_IMPL(OrderVarPordVertex, OrderVarVertex)
public:
// CONSTRUCTOR
OrderVarPordVertex(OrderGraph* graphp, AstVarScope* vscp)
@ -256,6 +263,7 @@ public:
// Edge type
class OrderEdge final : public V3GraphEdge {
VL_RTTI_IMPL(OrderEdge, V3GraphEdge)
friend class OrderGraph; // Only the OrderGraph can create these
// CONSTRUCTOR
OrderEdge(OrderGraph* graphp, OrderEitherVertex* fromp, OrderEitherVertex* top, int weight,

View File

@ -33,6 +33,7 @@
class OrderMoveDomScope;
class OrderMoveVertex final : public V3GraphVertex {
VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex)
enum OrderMState : uint8_t { POM_WAIT, POM_READY, POM_MOVED };
OrderLogicVertex* const m_logicp;
@ -92,6 +93,7 @@ public:
// Similar to OrderMoveVertex, but modified for threaded code generation.
class MTaskMoveVertex final : public V3GraphVertex {
VL_RTTI_IMPL(MTaskMoveVertex, V3GraphVertex)
// This could be more compact, since we know m_varp and m_logicp
// cannot both be set. Each MTaskMoveVertex represents a logic node
// or a var node, it can't be both.

View File

@ -448,10 +448,10 @@ private:
public:
// METHODS
SiblingMC* toSiblingMC(); // Instead of dynamic_cast
const SiblingMC* toSiblingMC() const; // Instead of dynamic_cast
MTaskEdge* toMTaskEdge(); // Instead of dynamic_cast
const MTaskEdge* toMTaskEdge() const; // Instead of dynamic_cast
SiblingMC* toSiblingMC(); // Instead of cast<>/as<>
const SiblingMC* toSiblingMC() const; // Instead of cast<>/as<>
MTaskEdge* toMTaskEdge(); // Instead of cast<>/as<>
const MTaskEdge* toMTaskEdge() const; // Instead of cast<>/as<>
bool mergeWouldCreateCycle() const; // Instead of virtual method
inline void rescore();
@ -511,6 +511,7 @@ static_assert(!std::is_polymorphic<SiblingMC>::value, "Should not have a vtable"
// GraphEdge for the MTask graph
class MTaskEdge final : public V3GraphEdge, public MergeCandidate {
VL_RTTI_IMPL(MTaskEdge, V3GraphEdge)
friend class LogicMTask;
template <GraphWay::en T_Way>
friend class PartPropagateCp;
@ -806,7 +807,7 @@ public:
UINFO(0, " Parallelism factor = " << parallelismFactor() << endl);
}
static uint32_t vertexCost(const V3GraphVertex* vertexp) {
return dynamic_cast<const AbstractMTask*>(vertexp)->cost();
return vertexp->as<const AbstractMTask>()->cost();
}
private:
@ -857,7 +858,7 @@ static void partInitCriticalPaths(V3Graph* mtasksp) {
// They would have been all zeroes on initial creation of the MTaskEdges.
for (V3GraphVertex* vxp = mtasksp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
MTaskEdge* const mtedgep = dynamic_cast<MTaskEdge*>(edgep);
MTaskEdge* const mtedgep = edgep->as<MTaskEdge>();
mtedgep->resetCriticalPaths();
}
}
@ -1966,7 +1967,7 @@ private:
void findAdjacentTasks(const OrderVarStdVertex* varVtxp, TasksByRank& tasksByRank) {
// Find all writer tasks for this variable, group by rank.
for (V3GraphEdge* edgep = varVtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
if (const auto* const logicVtxp = dynamic_cast<OrderLogicVertex*>(edgep->fromp())) {
if (const auto* const logicVtxp = edgep->fromp()->cast<OrderLogicVertex>()) {
LogicMTask* const writerMtaskp = static_cast<LogicMTask*>(logicVtxp->userp());
tasksByRank[writerMtaskp->rank()].insert(writerMtaskp);
}
@ -2058,7 +2059,7 @@ public:
nextp = vtxp->verticesNextp();
// Only consider OrderVarStdVertex which reflects
// an actual lvalue assignment; the others do not.
if (const OrderVarStdVertex* const vvtxp = dynamic_cast<OrderVarStdVertex*>(vtxp)) {
if (const OrderVarStdVertex* const vvtxp = vtxp->cast<OrderVarStdVertex>()) {
if (vvtxp->vscp()->varp()->isSc()) {
systemCVars.push_back(vvtxp);
} else {
@ -2203,7 +2204,7 @@ public:
const uint32_t thisThreadId = threadId(mtaskp);
uint32_t result = 0;
for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep; edgep = edgep->inNextp()) {
const ExecMTask* const prevp = dynamic_cast<ExecMTask*>(edgep->fromp());
const ExecMTask* const prevp = edgep->fromp()->as<ExecMTask>();
if (threadId(prevp) != thisThreadId) ++result;
}
return result;
@ -2248,7 +2249,7 @@ void ThreadSchedule::dumpDotFile(const V3Graph& graph, const string& filename) c
// 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 = dynamic_cast<const ExecMTask*>(vxp)) {
if (const ExecMTask* const mtaskp = vxp->cast<const ExecMTask>()) {
minCost = minCost > mtaskp->cost() ? mtaskp->cost() : minCost;
}
}
@ -2271,13 +2272,13 @@ void ThreadSchedule::dumpDotFile(const V3Graph& graph, const string& filename) c
// Emit MTasks
for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
if (const ExecMTask* const mtaskp = dynamic_cast<const ExecMTask*>(vxp)) emitMTask(mtaskp);
if (const ExecMTask* const mtaskp = vxp->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 = dynamic_cast<const ExecMTask*>(vxp)) {
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";
@ -2365,7 +2366,7 @@ private:
bool isReady(ThreadSchedule& schedule, const ExecMTask* mtaskp) {
for (V3GraphEdge* edgeInp = mtaskp->inBeginp(); edgeInp; edgeInp = edgeInp->inNextp()) {
const ExecMTask* const prevp = dynamic_cast<ExecMTask*>(edgeInp->fromp());
const ExecMTask* const prevp = edgeInp->fromp()->as<const ExecMTask>();
if (schedule.threadId(prevp) == ThreadSchedule::UNASSIGNED) {
// This predecessor is not assigned yet
return false;
@ -2388,7 +2389,7 @@ public:
// Build initial ready list
for (V3GraphVertex* vxp = mtaskGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
ExecMTask* const mtaskp = dynamic_cast<ExecMTask*>(vxp);
ExecMTask* const mtaskp = vxp->as<ExecMTask>();
if (isReady(schedule, mtaskp)) readyMTasks.insert(mtaskp);
}
@ -2409,7 +2410,7 @@ public:
}
for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep;
edgep = edgep->inNextp()) {
const ExecMTask* const priorp = dynamic_cast<ExecMTask*>(edgep->fromp());
const ExecMTask* const priorp = edgep->fromp()->as<ExecMTask>();
const uint32_t priorEndTime = completionTime(schedule, priorp, threadId);
if (priorEndTime > timeBegin) timeBegin = priorEndTime;
}
@ -2449,7 +2450,7 @@ public:
UASSERT_OBJ(erased > 0, bestMtaskp, "Should have erased something?");
for (V3GraphEdge* edgeOutp = bestMtaskp->outBeginp(); edgeOutp;
edgeOutp = edgeOutp->outNextp()) {
ExecMTask* const nextp = dynamic_cast<ExecMTask*>(edgeOutp->top());
ExecMTask* const nextp = edgeOutp->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");
@ -2540,7 +2541,7 @@ void V3Partition::debugMTaskGraphStats(const V3Graph* graphp, const string& stag
for (const V3GraphVertex* mtaskp = graphp->verticesBeginp(); mtaskp;
mtaskp = mtaskp->verticesNextp()) {
++mtaskCount;
uint32_t mtaskCost = dynamic_cast<const AbstractMTask*>(mtaskp)->cost();
uint32_t mtaskCost = mtaskp->as<const AbstractMTask>()->cost();
totalCost += mtaskCost;
unsigned log2Cost = 0;
@ -2928,7 +2929,7 @@ static void fillinCosts(V3Graph* execMTaskGraphp) {
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
ExecMTask* const mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
ExecMTask* const mtp = const_cast<V3GraphVertex*>(vxp)->as<ExecMTask>();
// Compute name of mtask, for hash lookup
mtp->hashName(m_uniqueNames.get(mtp->bodyp()));
@ -2949,7 +2950,7 @@ static void fillinCosts(V3Graph* execMTaskGraphp) {
int missingProfiles = 0;
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
ExecMTask* const mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
ExecMTask* const mtp = const_cast<V3GraphVertex*>(vxp)->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);
@ -2978,14 +2979,14 @@ static void fillinCosts(V3Graph* execMTaskGraphp) {
static void finalizeCosts(V3Graph* execMTaskGraphp) {
GraphStreamUnordered ser(execMTaskGraphp, GraphWay::REVERSE);
while (const V3GraphVertex* const vxp = ser.nextp()) {
ExecMTask* const mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
ExecMTask* const mtp = const_cast<V3GraphVertex*>(vxp)->as<ExecMTask>();
// "Priority" is the critical path from the start of the mtask, to
// the end of the graph reachable from this mtask. Given the
// 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 = dynamic_cast<ExecMTask*>(edgep->top());
const ExecMTask* const followp = edgep->top()->as<ExecMTask>();
if ((followp->priority() + mtp->cost()) > mtp->priority()) {
mtp->priority(followp->priority() + mtp->cost());
}
@ -2996,7 +2997,7 @@ static void finalizeCosts(V3Graph* execMTaskGraphp) {
// (It's common for tasks to shrink to nothing when V3LifePost
// removes dly assignments.)
for (V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;) {
ExecMTask* const mtp = dynamic_cast<ExecMTask*>(vxp);
ExecMTask* const mtp = vxp->as<ExecMTask>();
vxp = vxp->verticesNextp(); // Advance before delete
// Don't rely on checking mtp->cost() == 0 to detect an empty task.
@ -3100,7 +3101,7 @@ static void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t th
// 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 = dynamic_cast<ExecMTask*>(edgep->top());
const ExecMTask* const nextp = edgep->top()->as<ExecMTask>();
if (schedule.threadId(nextp) != threadId) {
addStrStmt("vlSelf->__Vm_mtaskstate_" + cvtToStr(nextp->id())
+ ".signalUpstreamDone(even_cycle);\n");

View File

@ -29,6 +29,7 @@
// MTasks and graph structures
class AbstractMTask VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(AbstractMTask, V3GraphVertex)
public:
explicit AbstractMTask(V3Graph* graphp)
: V3GraphVertex{graphp} {}
@ -38,6 +39,7 @@ public:
};
class AbstractLogicMTask VL_NOT_FINAL : public AbstractMTask {
VL_RTTI_IMPL(AbstractLogicMTask, AbstractMTask)
public:
// TYPES
using VxList = std::list<MTaskMoveVertex*>;
@ -53,6 +55,7 @@ public:
};
class ExecMTask final : public AbstractMTask {
VL_RTTI_IMPL(ExecMTask, AbstractMTask)
private:
AstMTaskBody* const m_bodyp; // Task body
const uint32_t m_id; // Unique id of this mtask.

131
src/V3Rtti.h Normal file
View File

@ -0,0 +1,131 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Simple and efficient Run-Time Type Information
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3RTTI_H_
#define VERILATOR_V3RTTI_H_
#include "verilatedos.h"
#include <cstdint>
#include <type_traits>
// Holds list of types as template parameter pack.
// Useful in compile-time code generation.
template <typename... TN>
struct VTypeList {
template <typename... UN>
constexpr VTypeList<TN..., UN...> operator+(VTypeList<UN...>) const {
return {};
}
};
// Holds one type.
// Can be safely used as a return or argument type, and even instantiated, without triggering any
// potential limitations or effects of the held type.
template <typename T>
struct VTypeWrapper {
using type_t = T;
};
// Implementation details of other constructs defined in this header.
namespace V3RttiInternal {
// Helper function for extracting first type from VTypeList.
template <typename T0, typename... TN>
static inline constexpr VTypeWrapper<T0> vlTypeListFront(VTypeList<T0, TN...>) {
return {};
}
// Overload for empty type list. Returns false.
inline static constexpr bool isClassIdOfOneOf(uintptr_t id, VTypeList<>) VL_PURE { return false; }
// Returns true iff `id` has the same value as `T::rttiClassId()`, where `T` is any type held by
// `VTypeList` object passed as the second argument.
template <typename Base0, typename... BaseN>
inline static constexpr bool isClassIdOfOneOf(uintptr_t id, VTypeList<Base0, BaseN...>) VL_PURE {
return id == Base0::rttiClassId() || isClassIdOfOneOf(id, VTypeList<BaseN...>{});
}
} // namespace V3RttiInternal
// Alias for the first (frontmost) type held by type list `TL`.
template <typename TL>
using VTypeListFront = typename decltype(::V3RttiInternal::vlTypeListFront(TL{}))::type_t;
// `VTypeList` holding types from type lists `TL1` followed by types from type list `TL2`.
template <typename TL1, typename TL2>
using VJoinedTypeLists = decltype(TL1{} + TL2{});
// Common code used by VL_RTTI_COMMON_IMPL and VL_RTTI_COMMON_IMPL_BASE.
#define V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass) \
private: \
/* A type used only for implementation of the static_assert below. */ \
struct RttiUniqueTypeForThisClass {}; \
static_assert( \
std::is_same<RttiUniqueTypeForThisClass, ThisClass::RttiUniqueTypeForThisClass>::value, \
"'ThisClass' argument (" #ThisClass ") does not match the class name"); \
\
public: \
/* Returns unique ID of the class. Useful with `isInstanceOfClassWithId()` method. */ \
static uintptr_t rttiClassId() VL_PURE { \
/* The only purpose of the following variable is to occupy an unique memory address. */ \
/* This address is used as an unique class ID. */ \
static char aStaticVariable; \
return reinterpret_cast<uintptr_t>(&aStaticVariable); \
}
// Call this macro at the beginning of class definition if the class derives from a
// class with VL_RTTI_IMPL or VL_RTTI_IMPL_BASE calls.
#define VL_RTTI_IMPL(ThisClass, DirectBaseClass) \
V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass) \
static_assert( \
std::is_same<DirectBaseClass, \
VTypeListFront<DirectBaseClass::RttiThisAndBaseClassesList>>::value, \
"Missing VL_RTTI_IMPL(...) in the direct base class (" #DirectBaseClass ")"); \
\
public: \
/* Type list containing this class and all classes from the inheritance chain. */ \
using RttiThisAndBaseClassesList \
= VJoinedTypeLists<VTypeList<ThisClass>, \
typename DirectBaseClass::RttiThisAndBaseClassesList>; \
\
protected: \
/* Returns true iff `id` has the same value as `T::rttiClassId()`, where `T` is either this \
* class or any class from this class' inheritance chain. */ \
bool isInstanceOfClassWithId(uintptr_t id) const override VL_PURE { \
return ::V3RttiInternal::isClassIdOfOneOf(id, RttiThisAndBaseClassesList{}); \
} \
\
private: /* Revert to private visibility after this macro */
// Call this macro at the beginning of a base class to implement class type queries using
// `p->isInstanceOfClassWithId(ClassName::rttiClassId())`.
#define VL_RTTI_IMPL_BASE(ThisClass) \
V3RTTIINTERNAL_VL_RTTI_COMMON_IMPL(ThisClass) \
public: \
/* Type list containing this class and all classes from the inheritance chain. */ \
using RttiThisAndBaseClassesList = VTypeList<ThisClass>; \
\
protected: \
/* Returns true iff `id` has the same value as value returned by this class' \
`rttiClassId()` method. */ \
virtual bool isInstanceOfClassWithId(uintptr_t id) const VL_PURE { \
return id == rttiClassId(); \
} \
\
private: /* Revert to private visibility after this macro */
#endif // Guard

View File

@ -59,17 +59,18 @@ namespace {
// ##############################################################################
// Data structures (graph types)
class LogicVertex final : public V3GraphVertex {
class SchedAcyclicLogicVertex final : public V3GraphVertex {
VL_RTTI_IMPL(SchedAcyclicLogicVertex, V3GraphVertex)
AstNode* const m_logicp; // The logic node this vertex represents
AstScope* const m_scopep; // The enclosing AstScope of the logic node
public:
LogicVertex(V3Graph* graphp, AstNode* logicp, AstScope* scopep)
SchedAcyclicLogicVertex(V3Graph* graphp, AstNode* logicp, AstScope* scopep)
: V3GraphVertex{graphp}
, m_logicp{logicp}
, m_scopep{scopep} {}
V3GraphVertex* clone(V3Graph* graphp) const override {
return new LogicVertex{graphp, logicp(), scopep()};
return new SchedAcyclicLogicVertex{graphp, logicp(), scopep()};
}
AstNode* logicp() const { return m_logicp; }
@ -81,16 +82,19 @@ public:
// LCOV_EXCL_STOP
};
class VarVertex final : public V3GraphVertex {
class SchedAcyclicVarVertex final : public V3GraphVertex {
VL_RTTI_IMPL(SchedAcyclicVarVertex, V3GraphVertex)
AstVarScope* const m_vscp; // The AstVarScope this vertex represents
public:
VarVertex(V3Graph* graphp, AstVarScope* vscp)
SchedAcyclicVarVertex(V3Graph* graphp, AstVarScope* vscp)
: V3GraphVertex{graphp}
, m_vscp{vscp} {}
AstVarScope* vscp() const { return m_vscp; }
AstVar* varp() const { return m_vscp->varp(); }
V3GraphVertex* clone(V3Graph* graphp) const override { return new VarVertex{graphp, vscp()}; }
V3GraphVertex* clone(V3Graph* graphp) const override {
return new SchedAcyclicVarVertex{graphp, vscp()};
}
// LCOV_EXCL_START // Debug code
string name() const override VL_MT_STABLE { return m_vscp->name(); }
@ -102,13 +106,12 @@ public:
class Graph final : public V3Graph {
void loopsVertexCb(V3GraphVertex* vtxp) override {
// TODO: 'typeName' is an internal thing. This should be more human readable.
if (LogicVertex* const lvtxp = dynamic_cast<LogicVertex*>(vtxp)) {
if (SchedAcyclicLogicVertex* const lvtxp = vtxp->cast<SchedAcyclicLogicVertex>()) {
AstNode* const logicp = lvtxp->logicp();
std::cerr << logicp->fileline()->warnOtherStandalone()
<< " Example path: " << logicp->typeName() << endl;
} else {
VarVertex* const vvtxp = dynamic_cast<VarVertex*>(vtxp);
UASSERT(vvtxp, "Cannot be anything else");
SchedAcyclicVarVertex* const vvtxp = vtxp->as<SchedAcyclicVarVertex>();
AstVarScope* const vscp = vvtxp->vscp();
std::cerr << vscp->fileline()->warnOtherStandalone()
<< " Example path: " << vscp->prettyName() << endl;
@ -125,8 +128,8 @@ std::unique_ptr<Graph> buildGraph(const LogicByScope& lbs) {
// AstVarScope::user1() -> VarVertx
const VNUser1InUse user1InUse;
const auto getVarVertex = [&](AstVarScope* vscp) {
if (!vscp->user1p()) vscp->user1p(new VarVertex{graphp.get(), vscp});
return vscp->user1u().to<VarVertex*>();
if (!vscp->user1p()) vscp->user1p(new SchedAcyclicVarVertex{graphp.get(), vscp});
return vscp->user1u().to<SchedAcyclicVarVertex*>();
};
const auto addEdge = [&](V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cuttable) {
@ -141,13 +144,14 @@ std::unique_ptr<Graph> buildGraph(const LogicByScope& lbs) {
// Can safely ignore Postponed as we generate them all
if (VN_IS(nodep, AlwaysPostponed)) continue;
LogicVertex* const lvtxp = new LogicVertex{graphp.get(), nodep, scopep};
SchedAcyclicLogicVertex* const lvtxp
= new SchedAcyclicLogicVertex{graphp.get(), nodep, scopep};
const VNUser2InUse user2InUse;
const VNUser3InUse user3InUse;
nodep->foreach([&](AstVarRef* refp) {
AstVarScope* const vscp = refp->varScopep();
VarVertex* const vvtxp = getVarVertex(vscp);
SchedAcyclicVarVertex* const vvtxp = getVarVertex(vscp);
// We want to cut the narrowest signals
const int weight = vscp->width() / 8 + 1;
// If written, add logic -> var edge
@ -208,7 +212,7 @@ void removeNonCyclic(Graph* graphp) {
}
// Has this VarVertex been cut? (any edges in or out has been cut)
bool isCut(const VarVertex* vtxp) {
bool isCut(const SchedAcyclicVarVertex* vtxp) {
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
if (edgep->weight() == 0) return true;
}
@ -218,33 +222,33 @@ bool isCut(const VarVertex* vtxp) {
return false;
}
std::vector<VarVertex*> findCutVertices(Graph* graphp) {
std::vector<VarVertex*> result;
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 (VarVertex* const vvtxp = dynamic_cast<VarVertex*>(vtxp)) {
if (SchedAcyclicVarVertex* const vvtxp = vtxp->cast<SchedAcyclicVarVertex>()) {
if (!vvtxp->vscp()->user1SetOnce() && isCut(vvtxp)) result.push_back(vvtxp);
}
}
return result;
}
void resetEdgeWeights(const std::vector<VarVertex*>& cutVertices) {
for (VarVertex* const vvtxp : cutVertices) {
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);
}
}
// A VarVertex together with its fanout
using Candidate = std::pair<VarVertex*, unsigned>;
using Candidate = std::pair<SchedAcyclicVarVertex*, unsigned>;
// Gather all splitting candidates that are in the same SCC as the given vertex
void gatherSCCCandidates(V3GraphVertex* vtxp, std::vector<Candidate>& candidates) {
if (vtxp->user()) return; // Already done
vtxp->user(true);
if (VarVertex* const vvtxp = dynamic_cast<VarVertex*>(vtxp)) {
if (SchedAcyclicVarVertex* const vvtxp = vtxp->cast<SchedAcyclicVarVertex>()) {
AstVar* const varp = vvtxp->varp();
const string name = varp->prettyName();
if (!varp->user3SetOnce() // Only consider each AstVar once
@ -270,7 +274,7 @@ void gatherSCCCandidates(V3GraphVertex* vtxp, std::vector<Candidate>& candidates
}
// Find all variables in a loop (SCC) that are candidates for splitting to break loops.
void reportLoopVars(Graph* graphp, VarVertex* vvtxp) {
void reportLoopVars(Graph* graphp, SchedAcyclicVarVertex* vvtxp) {
// Vector of variables in UNOPTFLAT loop that are candidates for splitting.
std::vector<Candidate> candidates;
{
@ -325,8 +329,8 @@ void reportLoopVars(Graph* graphp, VarVertex* vvtxp) {
V3Stats::addStat("Scheduling, split_var, candidates", splittable);
}
void reportCycles(Graph* graphp, const std::vector<VarVertex*>& cutVertices) {
for (VarVertex* vvtxp : cutVertices) {
void reportCycles(Graph* graphp, const std::vector<SchedAcyclicVarVertex*>& cutVertices) {
for (SchedAcyclicVarVertex* vvtxp : cutVertices) {
AstVarScope* const vscp = vvtxp->vscp();
FileLine* const flp = vscp->fileline();
@ -350,16 +354,18 @@ void reportCycles(Graph* graphp, const std::vector<VarVertex*>& cutVertices) {
}
}
LogicByScope fixCuts(AstNetlist* netlistp, const std::vector<VarVertex*>& cutVertices) {
LogicByScope fixCuts(AstNetlist* netlistp,
const std::vector<SchedAcyclicVarVertex*>& cutVertices) {
// For all logic that reads a cut vertex, build a map from logic -> list of cut AstVarScope
// they read. Also build a vector of the involved logic for deterministic results.
std::unordered_map<LogicVertex*, std::vector<AstVarScope*>> lvtx2Cuts;
std::vector<LogicVertex*> lvtxps;
std::unordered_map<SchedAcyclicLogicVertex*, std::vector<AstVarScope*>> lvtx2Cuts;
std::vector<SchedAcyclicLogicVertex*> lvtxps;
{
const VNUser1InUse user1InUse; // bool: already added to 'lvtxps'
for (VarVertex* const vvtxp : cutVertices) {
for (SchedAcyclicVarVertex* const vvtxp : cutVertices) {
for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
LogicVertex* const lvtxp = static_cast<LogicVertex*>(edgep->top());
SchedAcyclicLogicVertex* const lvtxp
= static_cast<SchedAcyclicLogicVertex*>(edgep->top());
if (!lvtxp->logicp()->user1SetOnce()) lvtxps.push_back(lvtxp);
lvtx2Cuts[lvtxp].push_back(vvtxp->vscp());
}
@ -370,7 +376,7 @@ LogicByScope fixCuts(AstNetlist* netlistp, const std::vector<VarVertex*>& cutVer
// explicit additional triggers on the cut variables)
LogicByScope result;
SenTreeFinder finder{netlistp};
for (LogicVertex* const lvtxp : lvtxps) {
for (SchedAcyclicLogicVertex* const lvtxp : lvtxps) {
AstNode* const logicp = lvtxp->logicp();
logicp->unlinkFrBack();
FileLine* const flp = logicp->fileline();
@ -412,7 +418,7 @@ LogicByScope breakCycles(AstNetlist* netlistp, LogicByScope& combinationalLogic)
graphp->acyclic(&V3GraphEdge::followAlwaysTrue);
// Find all cut vertices
const std::vector<VarVertex*> cutVertices = findCutVertices(graphp.get());
const std::vector<SchedAcyclicVarVertex*> cutVertices = findCutVertices(graphp.get());
// Reset edge weights for reporting
resetEdgeWeights(cutVertices);

View File

@ -55,6 +55,7 @@ namespace V3Sched {
namespace {
class SchedSenVertex final : public V3GraphVertex {
VL_RTTI_IMPL(SchedSenVertex, V3GraphVertex)
const AstSenItem* const m_senItemp;
public:
@ -74,6 +75,7 @@ public:
};
class SchedLogicVertex final : public V3GraphVertex {
VL_RTTI_IMPL(SchedLogicVertex, V3GraphVertex)
AstScope* const m_scopep;
AstSenTree* const m_senTreep;
AstNode* const m_logicp;
@ -97,6 +99,7 @@ public:
};
class SchedVarVertex final : public V3GraphVertex {
VL_RTTI_IMPL(SchedVarVertex, V3GraphVertex)
const AstVarScope* const m_vscp;
public:
@ -299,7 +302,7 @@ void colorActiveRegion(const V3Graph& graph) {
// Trace from all SchedSenVertex
for (V3GraphVertex* vtxp = graph.verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
if (const auto activeEventVtxp = dynamic_cast<SchedSenVertex*>(vtxp)) {
if (const auto activeEventVtxp = vtxp->cast<SchedSenVertex>()) {
queue.push_back(activeEventVtxp);
}
}
@ -323,9 +326,9 @@ void colorActiveRegion(const V3Graph& graph) {
// 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 (dynamic_cast<SchedLogicVertex*>(&vtx)) {
if (vtx.is<SchedLogicVertex>()) {
for (V3GraphEdge* edgep = vtx.outBeginp(); edgep; edgep = edgep->outNextp()) {
UASSERT(dynamic_cast<SchedVarVertex*>(edgep->top()), "Should be var vertex");
UASSERT(edgep->top()->is<SchedVarVertex>(), "Should be var vertex");
queue.push_back(edgep->top());
}
}
@ -350,7 +353,7 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo
LogicRegions result;
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
if (const auto lvtxp = dynamic_cast<SchedLogicVertex*>(vtxp)) {
if (const auto lvtxp = vtxp->cast<SchedLogicVertex>()) {
LogicByScope& lbs = lvtxp->color() ? result.m_act : result.m_nba;
AstNode* const logicp = lvtxp->logicp();
logicp->unlinkFrBack();

View File

@ -59,11 +59,12 @@ enum RegionFlags : uint8_t {
//##############################################################################
// Data structures (graph types)
class Vertex VL_NOT_FINAL : public V3GraphVertex {
RegionFlags m_drivingRegions{NONE}; // The regions driving this vertex
class SchedReplicateVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(SchedReplicateVertex, V3GraphVertex)
RegionFlags m_drivingRegions{RegionFlags::NONE}; // The regions driving this vertex
public:
explicit Vertex(V3Graph* graphp)
explicit SchedReplicateVertex(V3Graph* graphp)
: V3GraphVertex{graphp} {}
uint8_t drivingRegions() const { return m_drivingRegions; }
void addDrivingRegions(uint8_t regions) {
@ -87,16 +88,17 @@ public:
// LCOV_EXCL_STOP
};
class LogicVertex final : public Vertex {
class SchedReplicateLogicVertex final : public SchedReplicateVertex {
VL_RTTI_IMPL(SchedReplicateLogicVertex, SchedReplicateVertex)
AstScope* const m_scopep; // The enclosing AstScope of the logic node
AstSenTree* const m_senTreep; // The sensitivity of the logic node
AstNode* const m_logicp; // The logic node this vertex represents
RegionFlags const m_assignedRegion; // The region this logic is originally assigned to
public:
LogicVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* senTreep, AstNode* logicp,
RegionFlags assignedRegion)
: Vertex{graphp}
SchedReplicateLogicVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* senTreep,
AstNode* logicp, RegionFlags assignedRegion)
: SchedReplicateVertex{graphp}
, m_scopep{scopep}
, m_senTreep{senTreep}
, m_logicp{logicp}
@ -113,12 +115,13 @@ public:
string dotShape() const override { return "rectangle"; }
};
class VarVertex final : public Vertex {
class SchedReplicateVarVertex final : public SchedReplicateVertex {
VL_RTTI_IMPL(SchedReplicateVarVertex, SchedReplicateVertex)
AstVarScope* const m_vscp; // The AstVarScope this vertex represents
public:
VarVertex(V3Graph* graphp, AstVarScope* vscp)
: Vertex{graphp}
SchedReplicateVarVertex(V3Graph* graphp, AstVarScope* vscp)
: SchedReplicateVertex{graphp}
, m_vscp{vscp} {
// Top level inputs are
if (varp()->isPrimaryInish() || varp()->isSigUserRWPublic() || varp()->isWrittenByDpi()) {
@ -149,11 +152,11 @@ std::unique_ptr<Graph> buildGraph(const LogicRegions& logicRegions) {
// AstVarScope::user1() -> VarVertx
const VNUser1InUse user1InUse;
const auto getVarVertex = [&](AstVarScope* vscp) {
if (!vscp->user1p()) vscp->user1p(new VarVertex{graphp.get(), vscp});
return vscp->user1u().to<VarVertex*>();
if (!vscp->user1p()) vscp->user1p(new SchedReplicateVarVertex{graphp.get(), vscp});
return vscp->user1u().to<SchedReplicateVarVertex*>();
};
const auto addEdge = [&](Vertex* fromp, Vertex* top) {
const auto addEdge = [&](SchedReplicateVertex* fromp, SchedReplicateVertex* top) {
new V3GraphEdge{graphp.get(), fromp, top, 1};
};
@ -182,14 +185,14 @@ std::unique_ptr<Graph> buildGraph(const LogicRegions& logicRegions) {
}
for (AstNode* nodep = activep->stmtsp(); nodep; nodep = nodep->nextp()) {
LogicVertex* const lvtxp
= new LogicVertex{graphp.get(), scopep, senTreep, nodep, region};
SchedReplicateLogicVertex* const lvtxp
= new SchedReplicateLogicVertex{graphp.get(), scopep, senTreep, nodep, region};
const VNUser2InUse user2InUse;
const VNUser3InUse user3InUse;
nodep->foreach([&](AstVarRef* refp) {
AstVarScope* const vscp = refp->varScopep();
VarVertex* const vvtxp = getVarVertex(vscp);
SchedReplicateVarVertex* const vvtxp = getVarVertex(vscp);
// If read, add var -> logic edge
// Note: Use same heuristic as ordering does to ignore written variables
@ -216,7 +219,7 @@ std::unique_ptr<Graph> buildGraph(const LogicRegions& logicRegions) {
return graphp;
}
void propagateDrivingRegions(Vertex* vtxp) {
void propagateDrivingRegions(SchedReplicateVertex* vtxp) {
// Note: The graph is always acyclic, so the recursion will terminate
// Nothing to do if already visited
@ -225,7 +228,7 @@ void propagateDrivingRegions(Vertex* vtxp) {
// Compute union of driving regions of all inputs
uint8_t drivingRegions = 0;
for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
Vertex* const srcp = static_cast<Vertex*>(edgep->fromp());
SchedReplicateVertex* const srcp = edgep->fromp()->as<SchedReplicateVertex>();
propagateDrivingRegions(srcp);
drivingRegions |= srcp->drivingRegions();
}
@ -240,7 +243,7 @@ void propagateDrivingRegions(Vertex* vtxp) {
LogicReplicas replicate(Graph* graphp) {
LogicReplicas result;
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
if (LogicVertex* const lvtxp = dynamic_cast<LogicVertex*>(vtxp)) {
if (SchedReplicateLogicVertex* const lvtxp = vtxp->cast<SchedReplicateLogicVertex>()) {
const auto replicateTo = [&](LogicByScope& lbs) {
lbs.add(lvtxp->scopep(), lvtxp->senTreep(), lvtxp->logicp()->cloneTree(false));
};
@ -264,7 +267,7 @@ LogicReplicas replicateLogic(LogicRegions& logicRegionsRegions) {
if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate");
// Propagate driving region flags
for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) {
propagateDrivingRegions(static_cast<Vertex*>(vtxp));
propagateDrivingRegions(vtxp->as<SchedReplicateVertex>());
}
// Dump for debug
if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate-propagated");

View File

@ -99,6 +99,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Support classes
class SplitNodeVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(SplitNodeVertex, V3GraphVertex)
AstNode* const m_nodep;
protected:
@ -117,6 +118,7 @@ public:
};
class SplitPliVertex final : public SplitNodeVertex {
VL_RTTI_IMPL(SplitPliVertex, SplitNodeVertex)
public:
explicit SplitPliVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
@ -126,6 +128,7 @@ public:
};
class SplitLogicVertex final : public SplitNodeVertex {
VL_RTTI_IMPL(SplitLogicVertex, SplitNodeVertex)
public:
SplitLogicVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
@ -134,6 +137,7 @@ public:
};
class SplitVarStdVertex final : public SplitNodeVertex {
VL_RTTI_IMPL(SplitVarStdVertex, SplitNodeVertex)
public:
SplitVarStdVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
@ -142,6 +146,7 @@ public:
};
class SplitVarPostVertex final : public SplitNodeVertex {
VL_RTTI_IMPL(SplitVarPostVertex, SplitNodeVertex)
public:
SplitVarPostVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
@ -154,6 +159,7 @@ public:
// Edge types
class SplitEdge VL_NOT_FINAL : public V3GraphEdge {
VL_RTTI_IMPL(SplitEdge, V3GraphEdge)
uint32_t m_ignoreInStep = 0; // Step number that if set to, causes this edge to be ignored
static uint32_t s_stepNum; // Global step number
protected:
@ -170,14 +176,12 @@ public:
void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; }
virtual bool followScoreboard() const = 0;
static bool followScoreboard(const V3GraphEdge* edgep) {
const SplitEdge* const oedgep = dynamic_cast<const SplitEdge*>(edgep);
if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type");
const SplitEdge* const oedgep = edgep->as<SplitEdge>();
if (oedgep->ignoreThisStep()) return false;
return oedgep->followScoreboard();
}
static bool followCyclic(const V3GraphEdge* edgep) {
const SplitEdge* const oedgep = dynamic_cast<const SplitEdge*>(edgep);
if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type");
const SplitEdge* const oedgep = edgep->as<SplitEdge>();
return (!oedgep->ignoreThisStep());
}
string dotStyle() const override {
@ -187,6 +191,7 @@ public:
uint32_t SplitEdge::s_stepNum = 0;
class SplitPostEdge final : public SplitEdge {
VL_RTTI_IMPL(SplitPostEdge, SplitEdge)
public:
SplitPostEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
@ -196,6 +201,7 @@ public:
};
class SplitLVEdge final : public SplitEdge {
VL_RTTI_IMPL(SplitLVEdge, SplitEdge)
public:
SplitLVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
@ -205,6 +211,7 @@ public:
};
class SplitRVEdge final : public SplitEdge {
VL_RTTI_IMPL(SplitRVEdge, SplitEdge)
public:
SplitRVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
@ -213,7 +220,8 @@ public:
string dotColor() const override { return "green"; }
};
struct SplitScorebdEdge : public SplitEdge {
class SplitScorebdEdge final : public SplitEdge {
VL_RTTI_IMPL(SplitScorebdEdge, SplitEdge)
public:
SplitScorebdEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
@ -222,7 +230,8 @@ public:
string dotColor() const override { return "blue"; }
};
struct SplitStrictEdge : public SplitEdge {
class SplitStrictEdge final : public SplitEdge {
VL_RTTI_IMPL(SplitStrictEdge, SplitEdge)
// A strict order, based on the original statement order in the graph
// The only non-cutable edge type
public:
@ -316,14 +325,14 @@ protected:
void pruneDepsOnInputs() {
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
if (!vertexp->outBeginp() && dynamic_cast<SplitVarStdVertex*>(vertexp)) {
if (!vertexp->outBeginp() && vertexp->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("- ");
}
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
SplitEdge* const oedgep = dynamic_cast<SplitEdge*>(edgep);
SplitEdge* const oedgep = edgep->as<SplitEdge>();
oedgep->setIgnoreThisStep();
}
}
@ -474,17 +483,16 @@ protected:
// vertexes not involved with this step as unimportant
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
if (const SplitLogicVertex* const vvertexp
= dynamic_cast<SplitLogicVertex*>(vertexp)) {
if (!vvertexp->user()) {
if (!vertexp->user()) {
if (const SplitLogicVertex* const vvertexp = vertexp->cast<SplitLogicVertex>()) {
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep;
edgep = edgep->inNextp()) {
SplitEdge* const oedgep = dynamic_cast<SplitEdge*>(edgep);
SplitEdge* const oedgep = edgep->as<SplitEdge>();
oedgep->setIgnoreThisStep();
}
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep;
edgep = edgep->outNextp()) {
SplitEdge* const oedgep = dynamic_cast<SplitEdge*>(edgep);
SplitEdge* const oedgep = edgep->as<SplitEdge>();
oedgep->setIgnoreThisStep();
}
}
@ -903,7 +911,7 @@ protected:
// inputs) prune all edges that depend on the 'if'.
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
const SplitLogicVertex* const logicp = dynamic_cast<SplitLogicVertex*>(vertexp);
const SplitLogicVertex* const logicp = vertexp->cast<const SplitLogicVertex>();
if (!logicp) continue;
const AstNodeIf* const ifNodep = VN_CAST(logicp->nodep(), NodeIf);
@ -911,7 +919,7 @@ protected:
bool pruneMe = true;
for (V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) {
const SplitEdge* const oedgep = dynamic_cast<SplitEdge*>(edgep);
const SplitEdge* const oedgep = edgep->as<const SplitEdge>();
if (!oedgep->ignoreThisStep()) {
// This if conditional depends on something we can't
// prune -- a variable generated in the current block.
@ -921,7 +929,7 @@ protected:
// give a hint about why...
if (debug() >= 9) {
V3GraphVertex* vxp = oedgep->top();
const SplitNodeVertex* const nvxp = dynamic_cast<SplitNodeVertex*>(vxp);
const SplitNodeVertex* const nvxp = vxp->as<const SplitNodeVertex>();
UINFO(0, "Cannot prune if-node due to edge "
<< oedgep << " pointing to node " << nvxp->nodep() << endl);
nvxp->nodep()->dumpTree("- ");
@ -935,7 +943,7 @@ protected:
// This if can be split; prune dependencies on it.
for (V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) {
SplitEdge* const oedgep = dynamic_cast<SplitEdge*>(edgep);
SplitEdge* const oedgep = edgep->as<SplitEdge>();
oedgep->setIgnoreThisStep();
}
}

View File

@ -54,6 +54,7 @@ static void selfTestString();
// Vertex that tracks a per-vertex key
template <typename T_Key>
class TspVertexTmpl final : public V3GraphVertex {
VL_RTTI_IMPL(TspVertexTmpl, V3GraphVertex)
private:
const T_Key m_key;

View File

@ -85,6 +85,7 @@ public:
};
class TaskEdge final : public V3GraphEdge {
VL_RTTI_IMPL(TaskEdge, V3GraphEdge)
public:
TaskEdge(V3Graph* graphp, TaskBaseVertex* fromp, TaskBaseVertex* top)
: V3GraphEdge{graphp, fromp, top, 1, false} {}

View File

@ -93,6 +93,7 @@ private:
// Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is
// suspendable, all its dependents should also be suspendable
class DepVtx VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(DepVtx, V3GraphVertex)
AstClass* const m_classp; // Class associated with a method
AstNode* const m_nodep; // AST node represented by this graph vertex
@ -121,6 +122,7 @@ private:
};
class SuspendDepVtx final : public DepVtx {
VL_RTTI_IMPL(SuspendDepVtx, DepVtx)
string dotColor() const override {
if (nodep()->user2() & T_SUSPENDER) return "red";
if (nodep()->user2() & T_SUSPENDEE) return "blue";
@ -134,6 +136,7 @@ private:
};
class NeedsProcDepVtx final : public DepVtx {
VL_RTTI_IMPL(NeedsProcDepVtx, DepVtx)
string dotColor() const override {
if (nodep()->user2() & T_CALLS_PROC_SELF) return "red";
if (nodep()->user2() & T_HAS_PROC) return "blue";

View File

@ -56,6 +56,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Graph vertexes
class TraceActivityVertex final : public V3GraphVertex {
VL_RTTI_IMPL(TraceActivityVertex, V3GraphVertex)
AstNode* const m_insertp;
int32_t m_activityCode;
bool m_slow; // If always slow, we can use the same code
@ -100,6 +101,7 @@ public:
};
class TraceCFuncVertex final : public V3GraphVertex {
VL_RTTI_IMPL(TraceCFuncVertex, V3GraphVertex)
AstCFunc* const m_nodep;
public:
@ -115,6 +117,7 @@ public:
};
class TraceTraceVertex final : public V3GraphVertex {
VL_RTTI_IMPL(TraceTraceVertex, V3GraphVertex)
AstTraceDecl* const m_nodep; // TRACEINC this represents
// nullptr, or other vertex with the real code() that duplicates this one
TraceTraceVertex* m_duplicatep = nullptr;
@ -137,6 +140,7 @@ public:
};
class TraceVarVertex final : public V3GraphVertex {
VL_RTTI_IMPL(TraceVarVertex, V3GraphVertex)
AstVarScope* const m_nodep;
public:
@ -204,8 +208,7 @@ private:
// Hash all of the values the traceIncs need
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
itp = itp->verticesNextp()) {
if (const TraceTraceVertex* const vvertexp
= dynamic_cast<const TraceTraceVertex*>(itp)) {
if (const TraceTraceVertex* const vvertexp = itp->cast<const TraceTraceVertex>()) {
const AstTraceDecl* const nodep = vvertexp->nodep();
if (nodep->valuep()) {
UASSERT_OBJ(nodep->valuep()->backp() == nodep, nodep,
@ -220,7 +223,7 @@ private:
}
// Find if there are any duplicates
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (TraceTraceVertex* const vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
if (TraceTraceVertex* const vvertexp = itp->cast<TraceTraceVertex>()) {
const AstTraceDecl* const nodep = vvertexp->nodep();
if (nodep->valuep() && !vvertexp->duplicatep()) {
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
@ -229,7 +232,7 @@ private:
= VN_AS(dupit->second->backp(), TraceDecl);
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
TraceTraceVertex* const dupvertexp
= dynamic_cast<TraceTraceVertex*>(dupDeclp->user1u().toGraphVertex());
= dupDeclp->user1u().toGraphVertex()->cast<TraceTraceVertex>();
UINFO(8, " Orig " << nodep << endl);
UINFO(8, " dup " << dupDeclp << endl);
// Mark the hashed node as the original and our
@ -246,7 +249,7 @@ private:
// Remove all variable nodes
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
nextp = itp->verticesNextp();
if (TraceVarVertex* const vvertexp = dynamic_cast<TraceVarVertex*>(itp)) {
if (TraceVarVertex* const vvertexp = itp->cast<TraceVarVertex>()) {
vvertexp->rerouteEdges(&m_graph);
vvertexp->unlinkDelete(&m_graph);
}
@ -258,7 +261,7 @@ private:
// Remove all Cfunc nodes
for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) {
nextp = itp->verticesNextp();
if (TraceCFuncVertex* const vvertexp = dynamic_cast<TraceCFuncVertex*>(itp)) {
if (TraceCFuncVertex* const vvertexp = itp->cast<TraceCFuncVertex>()) {
vvertexp->rerouteEdges(&m_graph);
vvertexp->unlinkDelete(&m_graph);
}
@ -271,16 +274,13 @@ private:
// 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
= dynamic_cast<const TraceTraceVertex*>(itp)) {
if (const TraceTraceVertex* const vvertexp = itp->cast<const TraceTraceVertex>()) {
// Search for the incoming always edge
const V3GraphEdge* alwaysEdgep = nullptr;
for (const V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;
edgep = edgep->inNextp()) {
const TraceActivityVertex* const actVtxp
= dynamic_cast<const TraceActivityVertex*>(edgep->fromp());
UASSERT_OBJ(actVtxp, vvertexp->nodep(),
"Tracing a node with FROM non activity");
= edgep->fromp()->as<const TraceActivityVertex>();
if (actVtxp->activityAlways()) {
alwaysEdgep = edgep;
break;
@ -299,7 +299,7 @@ private:
// 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 = dynamic_cast<TraceActivityVertex*>(itp)) {
if (TraceActivityVertex* const vtxp = itp->cast<TraceActivityVertex>()) {
// Leave in the always vertex for later use.
if (vtxp != m_alwaysVtxp && !vtxp->outBeginp()) vtxp->unlinkDelete(&m_graph);
}
@ -309,7 +309,7 @@ private:
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 = dynamic_cast<TraceActivityVertex*>(itp)) {
if (TraceActivityVertex* const vvertexp = itp->cast<TraceActivityVertex>()) {
if (vvertexp != m_alwaysVtxp) {
if (vvertexp->slow()) {
vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW);
@ -328,14 +328,14 @@ private:
nFullCodes = 0;
nChgCodes = 0;
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (TraceTraceVertex* const vtxp = dynamic_cast<TraceTraceVertex*>(itp)) {
if (TraceTraceVertex* const vtxp = itp->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()) {
const TraceActivityVertex* const cfvertexp
= dynamic_cast<const TraceActivityVertex*>(edgep->fromp());
= edgep->fromp()->cast<const TraceActivityVertex>();
UASSERT_OBJ(cfvertexp, vtxp->nodep(),
"Should have been function pointing to this trace");
UINFO(9, " Activity: " << cfvertexp << endl);
@ -471,8 +471,7 @@ private:
// Insert activity setters
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
itp = itp->verticesNextp()) {
if (const TraceActivityVertex* const vtxp
= dynamic_cast<const TraceActivityVertex*>(itp)) {
if (const TraceActivityVertex* const vtxp = itp->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.
@ -787,7 +786,7 @@ private:
TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) {
TraceCFuncVertex* vertexp
= dynamic_cast<TraceCFuncVertex*>(nodep->user1u().toGraphVertex());
= nodep->user1() ? nodep->user1u().toGraphVertex()->cast<TraceCFuncVertex>() : nullptr;
if (!vertexp) {
vertexp = new TraceCFuncVertex{&m_graph, nodep};
nodep->user1p(vertexp);
@ -796,7 +795,8 @@ private:
}
TraceActivityVertex* getActivityVertexp(AstNode* nodep, bool slow) {
TraceActivityVertex* vertexp
= dynamic_cast<TraceActivityVertex*>(nodep->user3u().toGraphVertex());
= nodep->user3() ? nodep->user3u().toGraphVertex()->cast<TraceActivityVertex>()
: nullptr;
if (!vertexp) {
vertexp = new TraceActivityVertex{&m_graph, nodep, slow};
nodep->user3p(vertexp);

View File

@ -142,6 +142,7 @@ public:
// Graph support classes
class TristateVertex final : public V3GraphVertex {
VL_RTTI_IMPL(TristateVertex, V3GraphVertex)
AstNode* const m_nodep;
bool m_isTristate = false; // Logic indicates a tristate
bool m_feedsTri = false; // Propagates to a tristate node (on RHS)
@ -222,7 +223,7 @@ private:
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 = dynamic_cast<TristateVertex*>(edgep->top());
TristateVertex* const vvertexp = edgep->top()->as<TristateVertex>();
// 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()) {
@ -234,7 +235,7 @@ private:
// 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 = dynamic_cast<TristateVertex*>(edgep->fromp());
TristateVertex* const vvertexp = edgep->fromp()->as<TristateVertex>();
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
@ -259,7 +260,7 @@ private:
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 = dynamic_cast<TristateVertex*>(edgep->fromp());
TristateVertex* const vvertexp = edgep->fromp()->as<TristateVertex>();
// 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()) {