diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 16ccb332a..613b29e7d 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -97,6 +97,16 @@ std::unique_ptr DfgGraph::clone() const { vtxp2clonep.emplace(&vtx, cp); break; } + case VDfgType::atSpliceArray: { + DfgSpliceArray* const cp = new DfgSpliceArray{*clonep, vtx.fileline(), vtx.dtypep()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } + case VDfgType::atSplicePacked: { + DfgSplicePacked* const cp = new DfgSplicePacked{*clonep, vtx.fileline(), vtx.dtypep()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } default: { vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName()); VL_UNREACHABLE; @@ -109,49 +119,55 @@ std::unique_ptr DfgGraph::clone() const { // Constants have no inputs // Hook up inputs of cloned variables for (const DfgVertexVar& vtx : m_varVertices) { - switch (vtx.type()) { - case VDfgType::atVarArray: { - const DfgVarArray* const vp = vtx.as(); - DfgVarArray* const cp = vtxp2clonep.at(vp)->as(); - vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcp = edge.sourcep()) { - cp->addDriver(vp->driverFileLine(i), // - vp->driverIndex(i), // - vtxp2clonep.at(srcp)); - } - }); - break; - } - case VDfgType::atVarPacked: { - const DfgVarPacked* const vp = vtx.as(); - DfgVarPacked* const cp = vtxp2clonep.at(vp)->as(); - vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { - if (DfgVertex* const srcp = edge.sourcep()) { - cp->addDriver(vp->driverFileLine(i), // - vp->driverLsb(i), // - vtxp2clonep.at(srcp)); - } - }); - break; - } - default: { - vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName()); - VL_UNREACHABLE; - break; - } + // All variable vertices are unary + if (DfgVertex* const srcp = vtx.srcp()) { + vtxp2clonep.at(&vtx)->as()->srcp(vtxp2clonep.at(srcp)); } } // Hook up inputs of cloned operation vertices for (const DfgVertex& vtx : m_opVertices) { - DfgVertex* const cp = vtxp2clonep.at(&vtx); - // The code below doesn't work for DfgVertexVariadic, but none of the opVertices are such. - UASSERT_OBJ(!vtx.is(), &vtx, "DfgVertexVariadic not handled"); - const auto oSourceEdges = vtx.sourceEdges(); - auto cSourceEdges = cp->sourceEdges(); - UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx, "Mismatched source count"); - for (size_t i = 0; i < oSourceEdges.second; ++i) { - if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { - cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp)); + if (vtx.is()) { + switch (vtx.type()) { + case VDfgType::atSpliceArray: { + const DfgSpliceArray* const vp = vtx.as(); + DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as(); + vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + if (DfgVertex* const srcp = edge.sourcep()) { + cp->addDriver(vp->driverFileLine(i), // + vp->driverIndex(i), // + vtxp2clonep.at(srcp)); + } + }); + break; + } + case VDfgType::atSplicePacked: { + const DfgSplicePacked* const vp = vtx.as(); + DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as(); + vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { + if (DfgVertex* const srcp = edge.sourcep()) { + cp->addDriver(vp->driverFileLine(i), // + vp->driverLsb(i), // + vtxp2clonep.at(srcp)); + } + }); + break; + } + default: { + vtx.v3fatalSrc("Unhandled DfgVertexVariadic sub type: " + vtx.typeName()); + VL_UNREACHABLE; + break; + } + } + } else { + DfgVertex* const cp = vtxp2clonep.at(&vtx); + const auto oSourceEdges = vtx.sourceEdges(); + auto cSourceEdges = cp->sourceEdges(); + UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx, + "Mismatched source count"); + for (size_t i = 0; i < oSourceEdges.second; ++i) { + if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) { + cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp)); + } } } } @@ -313,6 +329,25 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { return; } + if (vtx.is()) { + os << toDotId(vtx); + os << " [label=\"" << vtx.typeName() << "\n"; + if (const DfgSpliceArray* const sp = vtx.cast()) { + const int elements = VN_AS(sp->dtypep(), UnpackArrayDType)->elementsConst(); + os << "_[" << elements << "]"; + } else { + os << "W" << vtx.width(); + } + os << " / F" << vtx.fanout() << '"'; + if (vtx.hasMultipleSinks()) { + os << ", shape=doubleoctagon"; + } else { + os << ", shape=octagon"; + } + os << "]\n"; + return; + } + os << toDotId(vtx); os << " [label=\"" << vtx.typeName() << "\nW" << vtx.width() << " / F" << vtx.fanout() << '"'; if (vtx.hasMultipleSinks()) { @@ -336,7 +371,7 @@ static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx) vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { // if (edge.sourcep()) { string headLabel; - if (vtx.arity() > 1 || vtx.is()) headLabel = vtx.srcName(idx); + if (vtx.arity() > 1 || vtx.is()) headLabel = vtx.srcName(idx); dumpDotEdge(os, edge, headLabel); } }); @@ -521,24 +556,34 @@ bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; } V3Hash DfgVertex::selfHash() const { return V3Hash{}; } bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const { + // If same vertex, then equal if (this == &that) return true; + + // If different type, then not equal if (this->type() != that.type()) return false; + + // If different data type, then not equal if (this->dtypep() != that.dtypep()) return false; + + // If different number of inputs, then not equal + auto thisPair = this->sourceEdges(); + const DfgEdge* const thisSrcEdgesp = thisPair.first; + const size_t thisArity = thisPair.second; + auto thatPair = that.sourceEdges(); + const DfgEdge* const thatSrcEdgesp = thatPair.first; + const size_t thatArity = thatPair.second; + if (thisArity != thatArity) return false; + + // Check vertex specifics if (!this->selfEquals(that)) return false; + // Check sources const auto key = (this < &that) ? EqualsCache::key_type{this, &that} // : EqualsCache::key_type{&that, this}; // Note: the recursive invocation can cause a re-hash but that will not invalidate references uint8_t& result = cache[key]; if (!result) { result = 2; // Assume equals - auto thisPair = this->sourceEdges(); - const DfgEdge* const thisSrcEdgesp = thisPair.first; - const size_t thisArity = thisPair.second; - auto thatPair = that.sourceEdges(); - const DfgEdge* const thatSrcEdgesp = thatPair.first; - const size_t thatArity = thatPair.second; - UASSERT_OBJ(thisArity == thatArity, this, "Same type vertices must have same arity!"); for (size_t i = 0; i < thisArity; ++i) { const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep; const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep; @@ -561,7 +606,12 @@ V3Hash DfgVertex::hash() { // variables, which we rely on. if (!is()) { hash += m_type; - hash += width(); // Currently all non-variable vertices are packed, so this is safe + if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) { + hash += adtypep->elementsConst(); + // TODO: maybe include sub-dtype, but not hugely important at the moment + } else { + hash += width(); + } const auto pair = sourceEdges(); const DfgEdge* const edgesp = pair.first; const size_t arity = pair.second; @@ -579,19 +629,16 @@ uint32_t DfgVertex::fanout() const { return result; } -DfgVarPacked* DfgVertex::getResultVar() { - UASSERT_OBJ(!this->is(), this, "Arrays are not supported by " << __FUNCTION__); - +DfgVertexVar* DfgVertex::getResultVar() { // It's easy if the vertex is already a variable ... - if (DfgVarPacked* const varp = this->cast()) return varp; + if (DfgVertexVar* const varp = this->cast()) return varp; - // Inspect existing variables fully written by this vertex, and choose one - DfgVarPacked* resp = nullptr; + // Inspect existing variables written by this vertex, and choose one + DfgVertexVar* resp = nullptr; // cppcheck-has-bug-suppress constParameter this->forEachSink([&resp](DfgVertex& sink) { - DfgVarPacked* const varp = sink.cast(); + DfgVertexVar* const varp = sink.cast(); if (!varp) return; - if (!varp->isDrivenFullyByDfg()) return; // Ignore SystemC variables, they cannot participate in expressions or // be assigned rvalue expressions. if (varp->varp()->isSc()) return; @@ -694,6 +741,42 @@ bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as(); + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) { + if (driverIndex(i) != thatp->driverIndex(i)) return false; + } + return true; +} + +V3Hash DfgSpliceArray::selfHash() const { + V3Hash hash; + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) hash += driverIndex(i); + return hash; +} + +// DfgSplicePacked ---------- + +bool DfgSplicePacked::selfEquals(const DfgVertex& that) const { + const DfgSplicePacked* const thatp = that.as(); + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) { + if (driverLsb(i) != thatp->driverLsb(i)) return false; + } + return true; +} + +V3Hash DfgSplicePacked::selfHash() const { + V3Hash hash; + const size_t arity = this->arity(); + for (size_t i = 0; i < arity; ++i) hash += driverLsb(i); + return hash; +} + // DfgVertexVar ---------- bool DfgVertexVar::selfEquals(const DfgVertex& that) const { diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 4101b3573..be198cf47 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -238,8 +238,7 @@ public: // Width of result uint32_t width() const { - // This is a hot enough function that this is an expensive check, so in debug build only. - UDEBUGONLY(UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'");); + UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'"); return dtypep()->width(); } @@ -283,7 +282,7 @@ public: // Return a canonical variable vertex that holds the value of this vertex, // or nullptr if no such variable exists in the graph. This is O(fanout). - DfgVarPacked* getResultVar() VL_MT_DISABLED; + DfgVertexVar* getResultVar() VL_MT_DISABLED; // Cache type for 'scopep' below using ScopeCache = std::unordered_map; @@ -572,7 +571,7 @@ class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex { protected: DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep, - uint32_t initialCapacity = 1) + uint32_t initialCapacity) : DfgVertex{dfg, type, flp, dtypep} , m_srcsp{allocSources(initialCapacity)} , m_srcCap{initialCapacity} {} @@ -914,15 +913,14 @@ bool DfgVertex::isOnes() const { // Inline method definitions - for DfgVertexVar //------------------------------------------------------------------------------ -DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity) - : DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity} +DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp) + : DfgVertexUnary{dfg, type, varp->fileline(), dtypeFor(varp)} , m_varp{varp} , m_varScopep{nullptr} { UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph"); } -DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp, - uint32_t initialCapacity) - : DfgVertexVariadic{dfg, type, vscp->fileline(), dtypeFor(vscp), initialCapacity} +DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp) + : DfgVertexUnary{dfg, type, vscp->fileline(), dtypeFor(vscp)} , m_varp{vscp->varp()} , m_varScopep{vscp} { UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph"); diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index b0532ee95..dc121cf00 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -183,49 +183,37 @@ class AstToDfgVisitor final : public VNVisitor { return m_foundUnhandled; } - // Build DfgEdge representing the LValue assignment. Returns false if unsuccessful. - bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) { + DfgVertexSplice* convertLValue(AstNode* nodep) { + FileLine* const flp = nodep->fileline(); + if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) { m_foundUnhandled = false; visit(vrefp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) return false; - getVertex(vrefp)->template as()->addDriver(flp, 0, vtxp); - return true; - } - if (AstSel* const selp = VN_CAST(nodep, Sel)) { - AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); - const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); - if (!vrefp || !lsbp) { - ++m_ctx.m_nonRepLhs; - return false; + if (m_foundUnhandled) return nullptr; + DfgVertexVar* const vtxp = getVertex(vrefp)->template as(); + // Ensure driving splice vertex exists + if (!vtxp->srcp()) { + if (VN_IS(vtxp->dtypep(), UnpackArrayDType)) { + vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, vtxp->dtypep()}); + } else { + vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, vtxp->dtypep()}); + } } - m_foundUnhandled = false; - visit(vrefp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) return false; - getVertex(vrefp)->template as()->addDriver(flp, lsbp->toUInt(), vtxp); - return true; - } - if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) { - AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); - const AstConst* const idxp = VN_CAST(selp->bitp(), Const); - if (!vrefp || !idxp) { - ++m_ctx.m_nonRepLhs; - return false; - } - m_foundUnhandled = false; - visit(vrefp); - // cppcheck-has-bug-suppress knownConditionTrueFalse - if (m_foundUnhandled) return false; - getVertex(vrefp)->template as()->addDriver(flp, idxp->toUInt(), vtxp); - return true; + return vtxp->srcp()->as(); } + + ++m_ctx.m_nonRepLhs; + return nullptr; + } + + // Build DfgEdge representing the LValue assignment. Returns false if unsuccessful. + bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) { + // Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part if (AstConcat* const concatp = VN_CAST(nodep, Concat)) { AstNode* const lhsp = concatp->lhsp(); AstNode* const rhsp = concatp->rhsp(); - { + { // Convet LHS of concat FileLine* const lFlp = lhsp->fileline(); DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(lhsp)}; lVtxp->fromp(vtxp); @@ -233,7 +221,7 @@ class AstToDfgVisitor final : public VNVisitor { if (!convertAssignment(flp, lhsp, lVtxp)) return false; } - { + { // Convert RHS of concat FileLine* const rFlp = rhsp->fileline(); DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(rhsp)}; rVtxp->fromp(vtxp); @@ -241,7 +229,37 @@ class AstToDfgVisitor final : public VNVisitor { return convertAssignment(flp, rhsp, rVtxp); } } - ++m_ctx.m_nonRepLhs; + + if (AstSel* const selp = VN_CAST(nodep, Sel)) { + AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); + const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const); + if (!vrefp || !lsbp) { + ++m_ctx.m_nonRepLhs; + return false; + } + if (DfgVertexSplice* const splicep = convertLValue(vrefp)) { + splicep->template as()->addDriver(flp, lsbp->toUInt(), vtxp); + return true; + } + } else if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) { + AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef); + const AstConst* const idxp = VN_CAST(selp->bitp(), Const); + if (!vrefp || !idxp) { + ++m_ctx.m_nonRepLhs; + return false; + } + if (DfgVertexSplice* const splicep = convertLValue(vrefp)) { + splicep->template as()->addDriver(flp, idxp->toUInt(), vtxp); + return true; + } + } else if (VN_IS(nodep, VarRef)) { + if (DfgVertexSplice* const splicep = convertLValue(nodep)) { + splicep->template as()->addDriver(flp, 0, vtxp); + return true; + } + } else { + ++m_ctx.m_nonRepLhs; + } return false; } @@ -315,18 +333,23 @@ class AstToDfgVisitor final : public VNVisitor { for (DfgVarPacked* const varp : m_varPackedps) { // Delete variables with no sinks nor sources (this can happen due to reverting // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && varp->arity() == 0) { + if (!varp->hasSinks() && !varp->srcp()) { VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); continue; } + // Nothing to do for un-driven (input) variables + if (!varp->srcp()) continue; + + DfgSplicePacked* const splicep = varp->srcp()->as(); + // Gather (and unlink) all drivers std::vector drivers; - drivers.reserve(varp->arity()); - varp->forEachSourceEdge([this, varp, &drivers](DfgEdge& edge, size_t idx) { + drivers.reserve(splicep->arity()); + splicep->forEachSourceEdge([this, splicep, &drivers](DfgEdge& edge, size_t idx) { DfgVertex* const driverp = edge.sourcep(); UASSERT(driverp, "Should not have created undriven sources"); - addDriver(varp->driverFileLine(idx), varp->driverLsb(idx), driverp, drivers); + addDriver(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp, drivers); edge.unlinkSource(); }); @@ -424,10 +447,10 @@ class AstToDfgVisitor final : public VNVisitor { } // Reinsert drivers in order - varp->resetSources(); + splicep->resetSources(); for (const Driver& driver : drivers) { if (!driver.m_vtxp) break; // Stop at end of compacted list - varp->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); + splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp); } // Prune vertices potentially unused due to resolving multiple drivers. @@ -442,6 +465,15 @@ class AstToDfgVisitor final : public VNVisitor { vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); }); vtxp->unlinkDelete(*m_dfgp); } + + // If the whole variable is driven, remove the splice node + if (splicep->arity() == 1 // + && splicep->driverLsb(0) == 0 // + && splicep->source(0)->width() == varp->width()) { + varp->srcp(splicep->source(0)); + varp->driverFileLine(splicep->driverFileLine(0)); + splicep->unlinkDelete(*m_dfgp); + } } } @@ -450,7 +482,7 @@ class AstToDfgVisitor final : public VNVisitor { for (DfgVarArray* const varp : m_varArrayps) { // Delete variables with no sinks nor sources (this can happen due to reverting // uncommitted vertices, which does not remove variables) - if (!varp->hasSinks() && varp->arity() == 0) { + if (!varp->hasSinks() && !varp->srcp()) { VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp); } } diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index b85609e04..58405d8b5 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -363,7 +363,7 @@ class TraceDriver final : public DfgVisitor { UINFO(9, "TraceDriver - Unhandled vertex type: " << vtxp->typeName()); } - void visit(DfgVarPacked* vtxp) override { + void visit(DfgSplicePacked* vtxp) override { // Proceed with the driver that wholly covers the searched bits const auto pair = vtxp->sourceEdges(); for (size_t i = 0; i < pair.second; ++i) { @@ -378,6 +378,13 @@ class TraceDriver final : public DfgVisitor { } } + void visit(DfgVarPacked* vtxp) override { + if (DfgVertex* const srcp = vtxp->srcp()) { + SET_RESULT(trace(srcp, m_msb, m_lsb)); + return; + } + } + void visit(DfgConcat* vtxp) override { DfgVertex* const rhsp = vtxp->rhsp(); DfgVertex* const lhsp = vtxp->lhsp(); @@ -607,10 +614,7 @@ class IndependentBits final : public DfgVisitor { mask(vtxp).setAllBits1(); // intentionally not using MASK here } - void visit(DfgVarPacked* vtxp) override { - // The mask of the traced variable is known to be all ones - if (vtxp == m_varp) return; - + void visit(DfgSplicePacked* vtxp) override { // Combine the masks of all drivers V3Number& m = MASK(vtxp); vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { @@ -619,6 +623,14 @@ class IndependentBits final : public DfgVisitor { }); } + void visit(DfgVarPacked* vtxp) override { + // The mask of the traced variable is known to be all ones + if (vtxp == m_varp) return; + + // Combine the masks of all drivers + if (DfgVertex* const srcp = vtxp->srcp()) MASK(vtxp) = MASK(srcp); + } + void visit(DfgConcat* vtxp) override { const DfgVertex* const rhsp = vtxp->rhsp(); const DfgVertex* const lhsp = vtxp->lhsp(); @@ -732,6 +744,13 @@ class IndependentBits final : public DfgVisitor { DfgVertex* const currp = m_workList.front(); m_workList.pop_front(); + if (VN_IS(currp->dtypep(), UnpackArrayDType)) { + // For an unpacked array vertex, just enque it's sinks. + // (There can be no loops through arrays directly) + currp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); }); + continue; + } + // Grab current mask of item const V3Number& maskCurr = mask(currp); // Remember current mask @@ -764,16 +783,11 @@ public: // so bits reported dependent might not actually be, but all bits reported // independent are known to be so. static V3Number apply(DfgVarPacked* varp) { + UASSERT_OBJ(varp->srcp(), varp, "Don't call on undriven variable"); IndependentBits independentBits{varp}; - // Combine the masks of all drivers of the variable + // The mask represents the dependent bits, so invert it V3Number result{varp->fileline(), static_cast(varp->width()), 0}; - varp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { - const DfgVertex* const srcp = edge.sourcep(); - // The mask represents the dependent bits, so invert it - V3Number inverseMask{srcp->fileline(), static_cast(srcp->width()), 0}; - inverseMask.opNot(independentBits.mask(srcp)); - result.opSelInto(inverseMask, varp->driverLsb(i), srcp->width()); - }); + result.opNot(independentBits.mask(varp->srcp())); return result; } }; diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index cc9c3e76a..4647d1484 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -341,32 +341,20 @@ class ExtractCyclicComponents final { return *clonep; } - // Fix up non-variable sources of a DfgVertexVar that are in a different component, - // using the provided 'relink' callback - template - void fixSources(T_Vertex& vtx, std::function relink) { - static_assert(std::is_base_of::value, - "'Vertex' must be a 'DfgVertexVar'"); + // Fix edges that cross components + void fixEdges(DfgVertexVar& vtx) { const size_t component = state(vtx).component; - vtx.forEachSourceEdge([&](DfgEdge& edge, size_t idx) { - DfgVertex& source = *edge.sourcep(); - // DfgVertexVar sources are fixed up by `fixSinks` on those sources - if (source.is()) return; - const size_t sourceComponent = state(source).component; - // Same component is OK - if (sourceComponent == component) return; - // Unlink the source edge (source is reconnected by 'relink' - edge.unlinkSource(); - // Apply the fixup - // cppcheck-has-bug-suppress constVariable - DfgVertexVar& clone = getClone(vtx, sourceComponent); - relink(*(clone.as()), source, idx); - }); - } - // Fix up sinks of given variable vertex that are in a different component - void fixSinks(DfgVertexVar& vtx) { - const size_t component = state(vtx).component; + // All variable vertices have at most a single source, and only variable + // vertices can have multiple sinks, therefore the source must be either: + // - in the same component as the variable vertex + // - be a variable vertex itself, which might be in a different component + // The later case will be fixed up when handling the source variable + DfgVertex* const srcp = vtx.srcp(); + UASSERT_OBJ(!srcp || srcp->is() || state(*srcp).component == component, &vtx, + "Driver of DfgVertexVar must be in the same component"); + + // Fix up sinks in a differetn component vtx.forEachSinkEdge([&](DfgEdge& edge) { const size_t sinkComponent = state(*edge.sinkp()).component; // Same component is OK @@ -376,49 +364,6 @@ class ExtractCyclicComponents final { }); } - // Fix edges that cross components - void fixEdges(DfgVertexVar& vtx) { - if (DfgVarPacked* const vvtxp = vtx.cast()) { - fixSources( - *vvtxp, [&](DfgVarPacked& clone, DfgVertex& driver, size_t driverIdx) { - clone.addDriver(vvtxp->driverFileLine(driverIdx), // - vvtxp->driverLsb(driverIdx), &driver); - }); - fixSinks(*vvtxp); - return; - } - - if (DfgVarArray* const vvtxp = vtx.cast()) { - fixSources( // - *vvtxp, [&](DfgVarArray& clone, DfgVertex& driver, size_t driverIdx) { - clone.addDriver(vvtxp->driverFileLine(driverIdx), // - vvtxp->driverIndex(driverIdx), &driver); - }); - fixSinks(*vvtxp); - return; - } - } - - static void packSources(DfgGraph& dfg) { - // Remove undriven variable sources - for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) { - if (DfgVarPacked* const varp = vtxp->cast()) { - varp->packSources(); - if (!varp->hasSinks() && varp->arity() == 0) { - VL_DO_DANGLING(varp->unlinkDelete(dfg), varp); - } - continue; - } - if (DfgVarArray* const varp = vtxp->cast()) { - varp->packSources(); - if (!varp->hasSinks() && varp->arity() == 0) { - VL_DO_DANGLING(varp->unlinkDelete(dfg), varp); - } - continue; - } - } - } - template void moveVertices(DfgVertex::List& list) { for (DfgVertex* const vtxp : list.unlinkable()) { @@ -446,11 +391,6 @@ class ExtractCyclicComponents final { UASSERT_OBJ(component == state(snk).component, &vtx, "Edge crossing components without variable involvement"); }); - if (const DfgVertexVar* const vtxp = vtx.cast()) { - vtxp->forEachSourceEdge([](const DfgEdge& edge, size_t) { - UASSERT_OBJ(edge.sourcep(), edge.sinkp(), "Missing source on variable vertex"); - }); - } }); } @@ -489,11 +429,6 @@ class ExtractCyclicComponents final { if (&vtx == lastp) break; } - // Pack sources of variables to remove the now undriven inputs - // (cloning might have unlinked some of the inputs), - packSources(m_dfg); - for (const auto& dfgp : m_components) packSources(*dfgp); - // Check results for consistency if (VL_UNLIKELY(m_doExpensiveChecks)) { checkEdges(m_dfg); diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index f62f3f3a3..7c7ac3d88 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -201,45 +201,52 @@ class DfgToAstVisitor final : DfgVisitor { ++m_ctx.m_resultEquations; } - void convertVarDriver(const DfgVarPacked* dfgVarp) { - if (dfgVarp->isDrivenFullyByDfg()) { - // Whole variable is driven. Render driver and assign directly to whole variable. - FileLine* const flp = dfgVarp->driverFileLine(0); - AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->source(0)); - addResultEquation(dfgVarp, flp, lhsp, rhsp); - } else { + void convertPackedDriver(const DfgVarPacked* dfgVarp) { + if (DfgSplicePacked* const splicep = dfgVarp->srcp()->cast()) { // Variable is driven partially. Render each driver as a separate assignment. - dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { + splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); // Render the rhs expression AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); // Create select LValue - FileLine* const flp = dfgVarp->driverFileLine(idx); + FileLine* const flp = splicep->driverFileLine(idx); AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstConst* const lsbp = new AstConst{flp, dfgVarp->driverLsb(idx)}; + AstConst* const lsbp = new AstConst{flp, splicep->driverLsb(idx)}; const int width = static_cast(edge.sourcep()->width()); AstSel* const lhsp = new AstSel{flp, refp, lsbp, width}; // Add assignment of the value to the selected bits addResultEquation(dfgVarp, flp, lhsp, rhsp); }); + return; } + + // Whole variable is driven. Render driver and assign directly to whole variable. + FileLine* const flp + = dfgVarp->driverFileLine() ? dfgVarp->driverFileLine() : dfgVarp->fileline(); + AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; + AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->srcp()); + addResultEquation(dfgVarp, flp, lhsp, rhsp); } void convertArrayDiver(const DfgVarArray* dfgVarp) { - // Variable is driven partially. Assign from parts of the canonical var. - dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { - UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); - // Render the rhs expression - AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); - // Create select LValue - FileLine* const flp = dfgVarp->driverFileLine(idx); - AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; - AstConst* const idxp = new AstConst{flp, dfgVarp->driverIndex(idx)}; - AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp}; - // Add assignment of the value to the selected bits - addResultEquation(dfgVarp, flp, lhsp, rhsp); - }); + if (DfgSpliceArray* const splicep = dfgVarp->srcp()->cast()) { + // Variable is driven partially. Assign from parts of the canonical var. + splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { + UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources"); + // Render the rhs expression + AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep()); + // Create select LValue + FileLine* const flp = splicep->driverFileLine(idx); + AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE}; + AstConst* const idxp = new AstConst{flp, splicep->driverIndex(idx)}; + AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp}; + // Add assignment of the value to the selected bits + addResultEquation(dfgVarp, flp, lhsp, rhsp); + }); + return; + } + + UASSERT_OBJ(false, dfgVarp, "Should not have wholly driven arrays in Dfg"); } // VISITORS @@ -285,11 +292,11 @@ class DfgToAstVisitor final : DfgVisitor { // The graph must have been regularized, so we only need to render assignments for (DfgVertexVar& vtx : dfg.varVertices()) { // If there is no driver (this vertex is an input to the graph), then nothing to do. - if (!vtx.isDrivenByDfg()) continue; + if (!vtx.srcp()) continue; // Render packed variable assignments if (const DfgVarPacked* const dfgVarp = vtx.cast()) { - convertVarDriver(dfgVarp); + convertPackedDriver(dfgVarp); continue; } diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 5d1e86795..717012a10 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -172,7 +172,7 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) { // Don't inline SystemC variables, as SystemC types are not interchangeable with // internal types, and hence the variables are not interchangeable either. if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) { - DfgVertex* const driverp = varp->source(0); + DfgVertex* const driverp = varp->srcp(); // We must keep the original driver in certain cases, when swapping them would // not be functionally or technically (implementation reasons) equivalent: @@ -381,12 +381,14 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { // The index variable DfgVarPacked* const idxVtxp = [&]() { // If there is an existing result variable, use that, otherwise create a new variable - DfgVarPacked* varp = srcp->getResultVar(); - if (!varp) { + DfgVarPacked* varp = nullptr; + if (DfgVertexVar* const vp = srcp->getResultVar()) { + varp = vp->as(); + } else { const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables); varp = dfg.makeNewVar(flp, name, idxDTypep, nullptr)->as(); varp->varp()->isInternal(true); - varp->addDriver(flp, 0, srcp); + varp->srcp(srcp); } varp->setHasModRefs(); return varp; @@ -554,9 +556,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { if (!varp->hasModRefs()) { // If it is only referenced in this DFG, it can be removed ++ctx.m_varsRemoved; - varp->replaceWith(varp->source(0)); + varp->replaceWith(varp->srcp()); varp->nodep()->unlinkFrBack()->deleteTree(); - } else if (DfgVarPacked* const driverp = varp->source(0)->cast()) { + } else if (DfgVarPacked* const driverp = varp->srcp()->cast()) { // If it's driven from another variable, it can be replaced by that. However, we do not // want to propagate SystemC variables into the design. if (driverp->varp()->isSc()) continue; diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 8cbe90104..0e4f27967 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1202,11 +1202,15 @@ class V3DfgPeephole final : public DfgVisitor { void visit(DfgArraySel* vtxp) override { if (DfgConst* const idxp = vtxp->bitp()->cast()) { if (DfgVarArray* const varp = vtxp->fromp()->cast()) { - const size_t idx = idxp->toSizeT(); - if (DfgVertex* const driverp = varp->driverAt(idx)) { - APPLYING(INLINE_ARRAYSEL) { - replace(vtxp, driverp); - return; + if (varp->srcp()) { + if (DfgSpliceArray* const splicep = varp->srcp()->cast()) { + const size_t idx = idxp->toSizeT(); + if (DfgVertex* const driverp = splicep->driverAt(idx)) { + APPLYING(INLINE_ARRAYSEL) { + replace(vtxp, driverp); + return; + } + } } } } diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index 2b9c72b54..0bfa62a08 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -45,12 +45,23 @@ class DfgRegularize final { // Ensure intermediate values used multiple times are written to variables for (DfgVertex& vtx : m_dfg.opVertices()) { const bool needsIntermediateVariable = [&]() { + // Splice vertices represent partial assignments, so they need a variable + // iff and only if they have a non-variable sink. + if (vtx.is()) { + const bool hasNonVarSink + = vtx.findSink([](const DfgVertex& snk) { // + return !snk.is(); + }); + return hasNonVarSink; + } // Anything that drives an SC variable needs an intermediate, // as we can only assign simple variables to SC variables at runtime. const bool hasScSink = vtx.findSink([](const DfgVertexVar& var) { // return var.varp()->isSc(); }); if (hasScSink) return true; + // // Splice vertices always need a variable as they represent partial updates + // if (vtx.is()) return true; // Operations without multiple sinks need no variables if (!vtx.hasMultipleSinks()) return false; // Array selects need no variables, they are just memory references @@ -63,26 +74,21 @@ class DfgRegularize final { // This is an op that requires a result variable. Ensure it is // assigned to one, and redirect other sinks read that variable. - if (DfgVarPacked* const varp = vtx.getResultVar()) { - // A variable already exists - UASSERT_OBJ(varp->arity() == 1, varp, "Result variable with multiple drivers"); - FileLine* const flp = varp->driverFileLine(0); - varp->sourceEdge(0)->unlinkSource(); - varp->resetSources(); + if (DfgVertexVar* const varp = vtx.getResultVar()) { + varp->sourceEdge<0>()->unlinkSource(); vtx.replaceWith(varp); - varp->addDriver(flp, 0, &vtx); + varp->srcp(&vtx); } else { // Need to create an intermediate variable const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps); FileLine* const flp = vtx.fileline(); AstScope* const scopep = scoped ? vtx.scopep(scopeCache) : nullptr; - DfgVarPacked* const newp - = m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep)->as(); + DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep); ++m_nTmps; ++m_ctx.m_temporariesIntroduced; // Replace vertex with the variable and add back driver vtx.replaceWith(newp); - newp->addDriver(vtx.fileline(), 0, &vtx); + newp->srcp(&vtx); } } } diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 833c3c869..50dd9a070 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -38,22 +38,24 @@ // === Abstract base node types (DfgVertex*) =================================== -class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic { +class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary { AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex) AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned) 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 + // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr. + FileLine* m_driverFileLine = nullptr; bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED; V3Hash selfHash() const final VL_MT_DISABLED; public: - inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity); - inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp, uint32_t initialCapacity); + inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp); + inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp); ASTGEN_MEMBERS_DfgVertexVar; - bool isDrivenByDfg() const { return arity() > 0; } + const std::string srcName(size_t) const override { return ""; } AstVar* varp() const { return m_varp; } AstVarScope* varScopep() const { return m_varScopep; } @@ -68,6 +70,13 @@ public: void setHasExtRefs() { m_hasExtRefs = true; } bool hasNonLocalRefs() const { return hasDfgRefs() || hasModRefs() || hasExtRefs(); } + FileLine* driverFileLine() const { return m_driverFileLine; } + void driverFileLine(FileLine* flp) { m_driverFileLine = flp; } + + bool isDrivenFullyByDfg() const { + return srcp() && !srcp()->is() && !varp()->isForced(); + } + // Variable cannot be removed, even if redundant in the DfgGraph (might be used externally) bool keep() const { // Keep if referenced outside this module @@ -82,6 +91,12 @@ public: return false; } }; +class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic { +public: + DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexVariadic{dfg, type, flp, dtypep, 1u} {} + ASTGEN_MEMBERS_DfgVertexSplice; +}; // === Concrete node types ===================================================== @@ -184,14 +199,49 @@ class DfgVarArray final : public DfgVertexVar { public: DfgVarArray(DfgGraph& dfg, AstVar* varp) - : DfgVertexVar{dfg, dfgType(), varp, 4u} { + : DfgVertexVar{dfg, dfgType(), varp} { UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), varp, "Non array DfgVarArray"); } DfgVarArray(DfgGraph& dfg, AstVarScope* vscp) - : DfgVertexVar{dfg, dfgType(), vscp, 4u} { + : DfgVertexVar{dfg, dfgType(), vscp} { UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), vscp, "Non array DfgVarArray"); } ASTGEN_MEMBERS_DfgVarArray; +}; +class DfgVarPacked final : public DfgVertexVar { + friend class DfgVertex; + friend class DfgVisitor; + +public: + DfgVarPacked(DfgGraph& dfg, AstVar* varp) + : DfgVertexVar{dfg, dfgType(), varp} { + UASSERT_OBJ(!VN_IS(dtypep(), UnpackArrayDType), varp, "Array DfgVarPacked"); + } + DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp) + : DfgVertexVar{dfg, dfgType(), vscp} { + UASSERT_OBJ(!VN_IS(dtypep(), UnpackArrayDType), vscp, "Array DfgVarPacked"); + } + ASTGEN_MEMBERS_DfgVarPacked; +}; + +// === DfgVertexSplice === +class DfgSpliceArray final : public DfgVertexSplice { + friend class DfgVertex; + friend class DfgVisitor; + + using DriverData = std::pair; + + std::vector m_driverData; // Additional data associated with each driver + + bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; + V3Hash selfHash() const override VL_MT_DISABLED; + +public: + DfgSpliceArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexSplice{dfg, dfgType(), flp, dtypep} { + UASSERT_OBJ(VN_IS(dtypep, UnpackArrayDType), flp, "Non array DfgSpliceArray"); + } + ASTGEN_MEMBERS_DfgSpliceArray; void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) { m_driverData.emplace_back(flp, index); @@ -203,27 +253,6 @@ public: DfgVertexVariadic::resetSources(); } - // Remove undriven sources - void packSources() { - // Grab and reset the driver data - std::vector driverData; - driverData.swap(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; } @@ -234,26 +263,27 @@ public: return edgep ? edgep->sourcep() : nullptr; } - const string srcName(size_t idx) const override { return cvtToStr(driverIndex(idx)); } + const std::string srcName(size_t idx) const override { + return std::to_string(driverIndex(idx)); + } }; -class DfgVarPacked final : public DfgVertexVar { +class DfgSplicePacked final : public DfgVertexSplice { friend class DfgVertex; friend class DfgVisitor; using DriverData = std::pair; - std::vector m_driverData; // Additional data associate with each driver + std::vector m_driverData; // Additional data associated with each driver + + bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED; + V3Hash selfHash() const override VL_MT_DISABLED; public: - DfgVarPacked(DfgGraph& dfg, AstVar* varp) - : DfgVertexVar{dfg, dfgType(), varp, 1u} {} - DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp) - : DfgVertexVar{dfg, dfgType(), vscp, 1u} {} - ASTGEN_MEMBERS_DfgVarPacked; - - bool isDrivenFullyByDfg() const { - return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced(); + DfgSplicePacked(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) + : DfgVertexSplice{dfg, dfgType(), flp, dtypep} { + UASSERT_OBJ(!VN_IS(dtypep, UnpackArrayDType), flp, "Array DfgSplicePacked"); } + ASTGEN_MEMBERS_DfgSplicePacked; void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) { m_driverData.emplace_back(flp, lsb); @@ -265,33 +295,10 @@ public: DfgVertexVariadic::resetSources(); } - // Remove undriven sources - void packSources() { - // Grab and reset the driver data - std::vector driverData; - driverData.swap(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)); - } + const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); } }; #endif diff --git a/test_regress/t/t_dfg_break_cycles.v b/test_regress/t/t_dfg_break_cycles.v index fe46e2055..b7f461835 100644 --- a/test_regress/t/t_dfg_break_cycles.v +++ b/test_regress/t/t_dfg_break_cycles.v @@ -107,4 +107,8 @@ module t ( `signal(REPLICATE, 4); assign REPLICATE = rand_a[3:0] ^ ({2{REPLICATE[3:2]}} >> 2); + `signal(PARTIAL, 4); + assign PARTIAL[0] = rand_a[0]; + // PARTIAL[1] intentionally unconnected + assign PARTIAL[3:2] = rand_a[3:2] ^ {PARTIAL[2], PARTIAL[0]}; endmodule