diff --git a/src/V3Active.cpp b/src/V3Active.cpp index fc40166c0..54b110d00 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -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 }; diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index e1a9375a4..e99c1561f 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -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(itp); + GateVarVertex* const vvertexp = itp->cast(); // 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(vertexp)) { + if (const GateVarVertex* const vvertexp = vertexp->cast()) { 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(itp)) { + if (const GateVarVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (GateVarVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (GateVarVertex* const vvertexp = itp->cast()) { 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(oldedgep->fromp())) { + if (GateLogicVertex* const lvertexp = oldedgep->fromp()->cast()) { 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(itp)) { + if (GateVarVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (GateVarVertex* const vertp = itp->cast()) { const AstVarScope* const vsp = vertp->varScp(); if (vsp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) { if (vsp->varp()->width() > 1) { diff --git a/src/V3Graph.h b/src/V3Graph.h index a0482b4e3..2d277826c 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -22,6 +22,7 @@ #include "V3Error.h" #include "V3List.h" +#include "V3Rtti.h" #include @@ -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 + bool is() const { + static_assert(std::is_base_of::value, + "'T' must be a subtype of V3GraphVertex"); + static_assert(std::is_same::type, + VTypeListFront>::value, + "Missing VL_RTTI_IMPL(...) call in 'T'"); + return this->isInstanceOfClassWithId(T::rttiClassId()); + } + + // Return cast to subtype T and assert of that type + template + T* as() { + UASSERT_OBJ(is(), this, "V3GraphVertex is not of expected type"); + return static_cast(this); + } + template + const T* as() const { + UASSERT_OBJ(is(), this, "V3GraphVertex is not of expected type"); + return static_cast(this); + } + + // Return cast to subtype T, else nullptr if different type + template + T* cast() { + return is() ? static_cast(this) : nullptr; + } + template + const T* cast() const { + return is() ? static_cast(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 + bool is() const { + static_assert(std::is_base_of::value, + "'T' must be a subtype of V3GraphEdge"); + static_assert(std::is_same::type, + VTypeListFront>::value, + "Missing VL_RTTI_IMPL(...) call in 'T'"); + return this->isInstanceOfClassWithId(T::rttiClassId()); + } + + // Return cast to subtype T and assert of that type + template + T* as() { + UASSERT(is(), "V3GraphEdge is not of expected type"); + return static_cast(this); + } + template + const T* as() const { + UASSERT(is(), "V3GraphEdge is not of expected type"); + return static_cast(this); + } + + // Return cast to subtype T, else nullptr if different type + template + T* cast() { + return is() ? static_cast(this) : nullptr; + } + template + const T* cast() const { + return is() ? static_cast(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"; } diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp index ddae8c48b..4b15ca3dd 100644 --- a/src/V3GraphAcyc.cpp +++ b/src/V3GraphAcyc.cpp @@ -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; // List of orig edges, see also GraphAcyc's decl diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index 93ed93b89..0550e73c1 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -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: diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 56121b68e..f62882369 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -333,7 +333,7 @@ private: } for (V3GraphVertex* mtaskVxp = nodep->depGraphp()->verticesBeginp(); mtaskVxp; mtaskVxp = mtaskVxp->verticesNextp()) { - const ExecMTask* const mtaskp = dynamic_cast(mtaskVxp); + const ExecMTask* const mtaskp = mtaskVxp->as(); m_execMTaskp = mtaskp; m_sequence = 0; iterate(mtaskp->bodyp()); diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index d44ae8361..6087999b3 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -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(vertexp)) { + if (const LinkCellsVertex* const vvertexp = vertexp->cast()) { 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(itp)) { + if (const LinkCellsVertex* const vvertexp = itp->cast()) { // +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); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index ae0365019..066d5d5b4 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -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(itp)) { + if (OrderLogicVertex* const lvtxp = itp->cast()) { 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(itp)) { + if (OrderLogicVertex* const lvtxp = itp->cast()) { 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(itp); - UASSERT(vertexp, "Null or vertex not derived from EitherVertex"); + UASSERT(itp, "Vertex should not be null"); + OrderEitherVertex* const vertexp = itp->as(); 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(vertexp)) { + if (OrderLogicVertex* const lvtxp = vertexp->cast()) { 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(fromVertexp)) { + if (OrderVarVertex* const varVtxp = fromVertexp->cast()) { 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(itp)) { + if (OrderVarVertex* const vvertexp = itp->cast()) { string name(vvertexp->vscp()->prettyName()); - if (dynamic_cast(itp)) { + if (itp->is()) { name += " {PRE}"; - } else if (dynamic_cast(itp)) { + } else if (itp->is()) { name += " {POST}"; - } else if (dynamic_cast(itp)) { + } else if (itp->is()) { 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(edgep->fromp()); + const OrderVarVertex* const pre_varp = edgep->fromp()->cast(); 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(edgep->top()); + const OrderVarVertex* const post_varp = edgep->top()->cast(); if (!post_varp) continue; AstVar* const varp = post_varp->vscp()->varp(); varp->addConsumingMTaskId(mtaskId); diff --git a/src/V3OrderGraph.h b/src/V3OrderGraph.h index d51a540a0..af6788d6e 100644 --- a/src/V3OrderGraph.h +++ b/src/V3OrderGraph.h @@ -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, diff --git a/src/V3OrderMoveGraph.h b/src/V3OrderMoveGraph.h index fb54ac7a0..af5da4df0 100644 --- a/src/V3OrderMoveGraph.h +++ b/src/V3OrderMoveGraph.h @@ -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. diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index 4eeb0d6a2..ebc271a71 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -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::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 friend class PartPropagateCp; @@ -806,7 +807,7 @@ public: UINFO(0, " Parallelism factor = " << parallelismFactor() << endl); } static uint32_t vertexCost(const V3GraphVertex* vertexp) { - return dynamic_cast(vertexp)->cost(); + return vertexp->as()->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(edgep); + MTaskEdge* const mtedgep = edgep->as(); 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(edgep->fromp())) { + if (const auto* const logicVtxp = edgep->fromp()->cast()) { LogicMTask* const writerMtaskp = static_cast(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(vtxp)) { + if (const OrderVarStdVertex* const vvtxp = vtxp->cast()) { 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(edgep->fromp()); + const ExecMTask* const prevp = edgep->fromp()->as(); 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(vxp)) { + if (const ExecMTask* const mtaskp = vxp->cast()) { 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(vxp)) emitMTask(mtaskp); + if (const ExecMTask* const mtaskp = vxp->cast()) 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(vxp)) { + if (const ExecMTask* const mtaskp = vxp->cast()) { 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(edgeInp->fromp()); + const ExecMTask* const prevp = edgeInp->fromp()->as(); 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(vxp); + ExecMTask* const mtaskp = vxp->as(); 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(edgep->fromp()); + const ExecMTask* const priorp = edgep->fromp()->as(); 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(edgeOutp->top()); + ExecMTask* const nextp = edgeOutp->top()->as(); // 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(mtaskp)->cost(); + uint32_t mtaskCost = mtaskp->as()->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(const_cast(vxp)); + ExecMTask* const mtp = const_cast(vxp)->as(); // 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(const_cast(vxp)); + ExecMTask* const mtp = const_cast(vxp)->as(); 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(const_cast(vxp)); + ExecMTask* const mtp = const_cast(vxp)->as(); // "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(edgep->top()); + const ExecMTask* const followp = edgep->top()->as(); 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(vxp); + ExecMTask* const mtp = vxp->as(); 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(edgep->top()); + const ExecMTask* const nextp = edgep->top()->as(); if (schedule.threadId(nextp) != threadId) { addStrStmt("vlSelf->__Vm_mtaskstate_" + cvtToStr(nextp->id()) + ".signalUpstreamDone(even_cycle);\n"); diff --git a/src/V3PartitionGraph.h b/src/V3PartitionGraph.h index 0827ca381..eb1455e2a 100644 --- a/src/V3PartitionGraph.h +++ b/src/V3PartitionGraph.h @@ -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; @@ -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. diff --git a/src/V3Rtti.h b/src/V3Rtti.h new file mode 100644 index 000000000..f787ef8c7 --- /dev/null +++ b/src/V3Rtti.h @@ -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 +#include + +// Holds list of types as template parameter pack. +// Useful in compile-time code generation. +template +struct VTypeList { + template + constexpr VTypeList operator+(VTypeList) 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 +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 +static inline constexpr VTypeWrapper vlTypeListFront(VTypeList) { + 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 +inline static constexpr bool isClassIdOfOneOf(uintptr_t id, VTypeList) VL_PURE { + return id == Base0::rttiClassId() || isClassIdOfOneOf(id, VTypeList{}); +} + +} // namespace V3RttiInternal + +// Alias for the first (frontmost) type held by type list `TL`. +template +using VTypeListFront = typename decltype(::V3RttiInternal::vlTypeListFront(TL{}))::type_t; + +// `VTypeList` holding types from type lists `TL1` followed by types from type list `TL2`. +template +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::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(&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>::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, \ + 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; \ +\ +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 diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index 20cfc4c7b..769383678 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -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(vtxp)) { + if (SchedAcyclicLogicVertex* const lvtxp = vtxp->cast()) { AstNode* const logicp = lvtxp->logicp(); std::cerr << logicp->fileline()->warnOtherStandalone() << " Example path: " << logicp->typeName() << endl; } else { - VarVertex* const vvtxp = dynamic_cast(vtxp); - UASSERT(vvtxp, "Cannot be anything else"); + SchedAcyclicVarVertex* const vvtxp = vtxp->as(); AstVarScope* const vscp = vvtxp->vscp(); std::cerr << vscp->fileline()->warnOtherStandalone() << " Example path: " << vscp->prettyName() << endl; @@ -125,8 +128,8 @@ std::unique_ptr 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(); + if (!vscp->user1p()) vscp->user1p(new SchedAcyclicVarVertex{graphp.get(), vscp}); + return vscp->user1u().to(); }; const auto addEdge = [&](V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cuttable) { @@ -141,13 +144,14 @@ std::unique_ptr 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 findCutVertices(Graph* graphp) { - std::vector result; +std::vector findCutVertices(Graph* graphp) { + std::vector result; const VNUser1InUse user1InUse; // bool: already added to result for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - if (VarVertex* const vvtxp = dynamic_cast(vtxp)) { + if (SchedAcyclicVarVertex* const vvtxp = vtxp->cast()) { if (!vvtxp->vscp()->user1SetOnce() && isCut(vvtxp)) result.push_back(vvtxp); } } return result; } -void resetEdgeWeights(const std::vector& cutVertices) { - for (VarVertex* const vvtxp : cutVertices) { +void resetEdgeWeights(const std::vector& 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; +using Candidate = std::pair; // Gather all splitting candidates that are in the same SCC as the given vertex void gatherSCCCandidates(V3GraphVertex* vtxp, std::vector& candidates) { if (vtxp->user()) return; // Already done vtxp->user(true); - if (VarVertex* const vvtxp = dynamic_cast(vtxp)) { + if (SchedAcyclicVarVertex* const vvtxp = vtxp->cast()) { 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& 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 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& cutVertices) { - for (VarVertex* vvtxp : cutVertices) { +void reportCycles(Graph* graphp, const std::vector& 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& cutVertices) { } } -LogicByScope fixCuts(AstNetlist* netlistp, const std::vector& cutVertices) { +LogicByScope fixCuts(AstNetlist* netlistp, + const std::vector& 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> lvtx2Cuts; - std::vector lvtxps; + std::unordered_map> lvtx2Cuts; + std::vector 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(edgep->top()); + SchedAcyclicLogicVertex* const lvtxp + = static_cast(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& 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 cutVertices = findCutVertices(graphp.get()); + const std::vector cutVertices = findCutVertices(graphp.get()); // Reset edge weights for reporting resetEdgeWeights(cutVertices); diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 1f3a0075b..6299813b2 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -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(vtxp)) { + if (const auto activeEventVtxp = vtxp->cast()) { 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(&vtx)) { + if (vtx.is()) { for (V3GraphEdge* edgep = vtx.outBeginp(); edgep; edgep = edgep->outNextp()) { - UASSERT(dynamic_cast(edgep->top()), "Should be var vertex"); + UASSERT(edgep->top()->is(), "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(vtxp)) { + if (const auto lvtxp = vtxp->cast()) { LogicByScope& lbs = lvtxp->color() ? result.m_act : result.m_nba; AstNode* const logicp = lvtxp->logicp(); logicp->unlinkFrBack(); diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 961f0e29f..e47e2fad8 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -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 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(); + if (!vscp->user1p()) vscp->user1p(new SchedReplicateVarVertex{graphp.get(), vscp}); + return vscp->user1u().to(); }; - 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 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 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(edgep->fromp()); + SchedReplicateVertex* const srcp = edgep->fromp()->as(); 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(vtxp)) { + if (SchedReplicateLogicVertex* const lvtxp = vtxp->cast()) { 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(vtxp)); + propagateDrivingRegions(vtxp->as()); } // Dump for debug if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate-propagated"); diff --git a/src/V3Split.cpp b/src/V3Split.cpp index cbabdbffc..80eb67778 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -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(edgep); - if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); + const SplitEdge* const oedgep = edgep->as(); if (oedgep->ignoreThisStep()) return false; return oedgep->followScoreboard(); } static bool followCyclic(const V3GraphEdge* edgep) { - const SplitEdge* const oedgep = dynamic_cast(edgep); - if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); + const SplitEdge* const oedgep = edgep->as(); 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(vertexp)) { + if (!vertexp->outBeginp() && vertexp->is()) { if (debug() >= 9) { const SplitVarStdVertex* const stdp = static_cast(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(edgep); + SplitEdge* const oedgep = edgep->as(); 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(vertexp)) { - if (!vvertexp->user()) { + if (!vertexp->user()) { + if (const SplitLogicVertex* const vvertexp = vertexp->cast()) { for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - SplitEdge* const oedgep = dynamic_cast(edgep); + SplitEdge* const oedgep = edgep->as(); oedgep->setIgnoreThisStep(); } for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - SplitEdge* const oedgep = dynamic_cast(edgep); + SplitEdge* const oedgep = edgep->as(); 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(vertexp); + const SplitLogicVertex* const logicp = vertexp->cast(); 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(edgep); + const SplitEdge* const oedgep = edgep->as(); 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(vxp); + const SplitNodeVertex* const nvxp = vxp->as(); 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(edgep); + SplitEdge* const oedgep = edgep->as(); oedgep->setIgnoreThisStep(); } } diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index f8961e5eb..09774b8fc 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -54,6 +54,7 @@ static void selfTestString(); // Vertex that tracks a per-vertex key template class TspVertexTmpl final : public V3GraphVertex { + VL_RTTI_IMPL(TspVertexTmpl, V3GraphVertex) private: const T_Key m_key; diff --git a/src/V3Task.cpp b/src/V3Task.cpp index a2fc9b0a5..f7010fa7c 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -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} {} diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 5b8651d20..15b5a2e0d 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -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"; diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index ced3fc9fd..abd9b5ffd 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -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(itp)) { + if (const TraceTraceVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (TraceTraceVertex* const vvertexp = itp->cast()) { 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(dupDeclp->user1u().toGraphVertex()); + = dupDeclp->user1u().toGraphVertex()->cast(); 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(itp)) { + if (TraceVarVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (TraceCFuncVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (const TraceTraceVertex* const vvertexp = itp->cast()) { // 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(edgep->fromp()); - UASSERT_OBJ(actVtxp, vvertexp->nodep(), - "Tracing a node with FROM non activity"); + = edgep->fromp()->as(); 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(itp)) { + if (TraceActivityVertex* const vtxp = itp->cast()) { // 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(itp)) { + if (TraceActivityVertex* const vvertexp = itp->cast()) { 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(itp)) { + if (TraceTraceVertex* const vtxp = itp->cast()) { 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(edgep->fromp()); + = edgep->fromp()->cast(); 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(itp)) { + if (const TraceActivityVertex* const vtxp = itp->cast()) { 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(nodep->user1u().toGraphVertex()); + = nodep->user1() ? nodep->user1u().toGraphVertex()->cast() : 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(nodep->user3u().toGraphVertex()); + = nodep->user3() ? nodep->user3u().toGraphVertex()->cast() + : nullptr; if (!vertexp) { vertexp = new TraceActivityVertex{&m_graph, nodep, slow}; nodep->user3p(vertexp); diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 6ad090603..fdf303b28 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -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(edgep->top()); + TristateVertex* const vvertexp = edgep->top()->as(); // 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(edgep->fromp()); + TristateVertex* const vvertexp = edgep->fromp()->as(); 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(edgep->fromp()); + TristateVertex* const vvertexp = edgep->fromp()->as(); // 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()) {