// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: DfgVertex sub-classes // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2024 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 // //************************************************************************* // // This is a data-flow graph based representation of combinational logic, // the main difference from a V3Graph is that DfgVertex owns the storage // of it's input edges (operands/sources/arguments), and can access each // input edge directly by indexing, making modifications more efficient // than the linked list based structures used by V3Graph. // // A bulk of the DfgVertex sub-types are generated by astgen, and are // analogous to the corresponding AstNode sub-types. // // See also the internals documentation docs/internals.rst // //************************************************************************* #ifndef VERILATOR_V3DFGVERTICES_H_ #define VERILATOR_V3DFGVERTICES_H_ #ifndef VERILATOR_V3DFG_H_ #error "Use V3Dfg.h as the include" #include "V3Dfg.h" // This helps code analysis tools pick up symbols in V3Dfg.h #define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE #endif // === Abstract base node types (DfgVertex*) =================================== class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic { AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex) bool m_hasDfgRefs = false; // This AstVar is referenced in a different DFG of the module bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module bool m_hasExtRefs = false; // This AstVar is referenced from outside the module bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED; V3Hash selfHash() const final VL_MT_DISABLED; public: DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity) : DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity} , m_varp{varp} {} ASTGEN_MEMBERS_DfgVertexVar; bool isDrivenByDfg() const { return arity() > 0; } AstVar* varp() const { return m_varp; } bool hasDfgRefs() const { return m_hasDfgRefs; } void setHasDfgRefs() { m_hasDfgRefs = true; } bool hasModRefs() const { return m_hasModRefs; } void setHasModRefs() { m_hasModRefs = true; } bool hasExtRefs() const { return m_hasExtRefs; } void setHasExtRefs() { m_hasExtRefs = true; } bool hasNonLocalRefs() const { return hasDfgRefs() || hasModRefs() || hasExtRefs(); } // Variable cannot be removed, even if redundant in the DfgGraph (might be used externally) bool keep() const { // Keep if referenced outside this module if (hasExtRefs()) return true; // Keep if traced if (v3Global.opt.trace() && varp()->isTrace()) return true; // Keep if public if (varp()->isSigPublic()) return true; // Keep if written in non-DFG code if (varp()->user3()) return true; // Otherwise it can be removed return false; } }; // === Concrete node types ===================================================== // === DfgVertex === class DfgConst final : public DfgVertex { friend class DfgVertex; friend class DfgVisitor; V3Number m_num; // Constant value bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; V3Hash selfHash() const override VL_MT_DISABLED; public: DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num) : DfgVertex{dfg, dfgType(), flp, dtypeForWidth(num.width())} , m_num{num} {} DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value = 0) : DfgVertex{dfg, dfgType(), flp, dtypeForWidth(width)} , m_num{flp, static_cast(width), value} {} ASTGEN_MEMBERS_DfgConst; V3Number& num() { return m_num; } const V3Number& num() const { return m_num; } size_t toSizeT() const { if VL_CONSTEXPR_CXX17 (sizeof(size_t) > sizeof(uint32_t)) { return static_cast(num().toUQuad()); } return static_cast(num().toUInt()); } bool isZero() const { return num().isEqZero(); } bool isOnes() const { return num().isEqAllOnes(width()); } // Does this DfgConst have the given value? Note this is not easy to answer if wider than 32. bool hasValue(uint32_t value) const { return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32; } std::pair sourceEdges() override { return {nullptr, 0}; } std::pair sourceEdges() const override { return {nullptr, 0}; } const string srcName(size_t) const override { // LCOV_EXCL_START VL_UNREACHABLE; return ""; } // LCOV_EXCL_STOP }; // === DfgVertexBinary === class DfgMux final : public DfgVertexBinary { // AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and // 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for // the constant 'lsbp', and as 'DfgMux` for the non-constant 'lsbp'. public: DfgMux(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) : DfgVertexBinary{dfg, dfgType(), flp, dtypep} {} ASTGEN_MEMBERS_DfgMux; DfgVertex* fromp() const { return source<0>(); } void fromp(DfgVertex* vtxp) { relinkSource<0>(vtxp); } DfgVertex* lsbp() const { return source<1>(); } void lsbp(DfgVertex* vtxp) { relinkSource<1>(vtxp); } const string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; } }; // === DfgVertexUnary === class DfgSel final : public DfgVertexUnary { // AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and // 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for // the constant 'lsbp', and as 'DfgMux` for the non-constant 'lsbp'. uint32_t m_lsb = 0; // The LSB index bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; V3Hash selfHash() const override VL_MT_DISABLED; public: DfgSel(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) : DfgVertexUnary{dfg, dfgType(), flp, dtypep} {} ASTGEN_MEMBERS_DfgSel; DfgVertex* fromp() const { return source<0>(); } void fromp(DfgVertex* vtxp) { relinkSource<0>(vtxp); } uint32_t lsb() const { return m_lsb; } void lsb(uint32_t value) { m_lsb = value; } const string srcName(size_t) const override { return "fromp"; } }; // === DfgVertexVar === class DfgVarArray final : public DfgVertexVar { friend class DfgVertex; friend class DfgVisitor; using DriverData = std::pair; std::vector m_driverData; // Additional data associate with each driver public: DfgVarArray(DfgGraph& dfg, AstVar* varp) : DfgVertexVar{dfg, dfgType(), varp, 4u} { UASSERT_OBJ(VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType), varp, "Non array DfgVarArray"); } ASTGEN_MEMBERS_DfgVarArray; void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) { m_driverData.emplace_back(flp, index); DfgVertexVariadic::addSource()->relinkSource(vtxp); } void resetSources() { m_driverData.clear(); DfgVertexVariadic::resetSources(); } // Remove undriven sources void packSources() { // Grab and reset the driver data std::vector driverData{std::move(m_driverData)}; // Grab and unlink the sources std::vector sources{arity()}; forEachSourceEdge([&](DfgEdge& edge, size_t idx) { sources[idx] = edge.sourcep(); edge.unlinkSource(); }); DfgVertexVariadic::resetSources(); // Add back the driven sources for (size_t i = 0; i < sources.size(); ++i) { if (!sources[i]) continue; addDriver(driverData[i].first, driverData[i].second, sources[i]); } } FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; } uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; } DfgVertex* driverAt(size_t idx) const { const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge&, size_t i) { // return driverIndex(i) == idx; }); return edgep ? edgep->sourcep() : nullptr; } const string srcName(size_t idx) const override { return cvtToStr(driverIndex(idx)); } }; class DfgVarPacked final : public DfgVertexVar { friend class DfgVertex; friend class DfgVisitor; using DriverData = std::pair; std::vector m_driverData; // Additional data associate with each driver public: DfgVarPacked(DfgGraph& dfg, AstVar* varp) : DfgVertexVar{dfg, dfgType(), varp, 1u} {} ASTGEN_MEMBERS_DfgVarPacked; bool isDrivenFullyByDfg() const { return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced(); } void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) { m_driverData.emplace_back(flp, lsb); DfgVertexVariadic::addSource()->relinkSource(vtxp); } void resetSources() { m_driverData.clear(); DfgVertexVariadic::resetSources(); } // Remove undriven sources void packSources() { // Grab and reset the driver data std::vector driverData{std::move(m_driverData)}; // Grab and unlink the sources std::vector sources{arity()}; forEachSourceEdge([&](DfgEdge& edge, size_t idx) { sources[idx] = edge.sourcep(); edge.unlinkSource(); }); DfgVertexVariadic::resetSources(); // Add back the driven sources for (size_t i = 0; i < sources.size(); ++i) { if (!sources[i]) continue; addDriver(driverData[i].first, driverData[i].second, sources[i]); } } FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; } uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; } const string srcName(size_t idx) const override { return isDrivenFullyByDfg() ? "" : cvtToStr(driverLsb(idx)); } }; #endif