From 29a080dd9b1e75ff1f997304e108949f5b733f4e Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 6 Oct 2022 18:34:18 +0100 Subject: [PATCH] DFG: Special case representation of AstSel AstSel is a ternary node, 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'. --- src/V3Dfg.cpp | 86 ++++----- src/V3DfgAstToDfg.cpp | 38 +++- src/V3DfgDfgToAst.cpp | 16 ++ src/V3DfgPeephole.cpp | 412 +++++++++++++++++++----------------------- src/V3DfgVertices.h | 41 +++++ 5 files changed, 309 insertions(+), 284 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index c5be6ba5f..0a12fc8c7 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -539,12 +539,6 @@ void DfgGraph::runToFixedPoint(std::function f) { static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; } -static bool isSimpleSel(const DfgSel* vtxp) { - const DfgConst* const lp = vtxp->lsbp()->cast(); - const DfgConst* const wp = vtxp->widthp()->cast(); - return lp && wp && !lp->hasMultipleSinks() && !wp->hasMultipleSinks(); -} - // Dump one DfgVertex in Graphviz format static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { @@ -598,11 +592,6 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { } if (const DfgConst* const constVtxp = vtx.cast()) { - const bool feedsSimpleSel = !constVtxp->findSink([](const DfgVertex& v) { // - return !v.is() || !isSimpleSel(v.as()); - }); - if (feedsSimpleSel) return; // Will draw it in the sel node as it is very common - const V3Number& num = constVtxp->constp()->num(); os << toDotId(vtx); @@ -620,20 +609,18 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { } if (const DfgSel* const selVtxp = vtx.cast()) { - if (isSimpleSel(selVtxp)) { - const uint32_t lsb = selVtxp->lsbp()->as()->toU32(); - const uint32_t msb = lsb + selVtxp->width() - 1; - os << toDotId(vtx); - os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\nW" << vtx.width() << " / F" - << vtx.fanout() << '"'; - if (vtx.hasMultipleSinks()) { - os << ", shape=doublecircle"; - } else { - os << ", shape=circle"; - } - os << "]" << endl; - return; + const uint32_t lsb = selVtxp->lsb(); + const uint32_t msb = lsb + selVtxp->width() - 1; + os << toDotId(vtx); + os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\nW" << vtx.width() << " / F" + << vtx.fanout() << '"'; + if (vtx.hasMultipleSinks()) { + os << ", shape=doublecircle"; + } else { + os << ", shape=circle"; } + os << "]" << endl; + return; } os << toDotId(vtx); @@ -656,16 +643,6 @@ static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, const string& hea // Dump one DfgVertex and all of its source DfgEdges in Graphviz format static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx) { dumpDotVertex(os, vtx); - - if (const DfgSel* const selVtxp = vtx.cast()) { - if (isSimpleSel(selVtxp)) { - UASSERT_OBJ(selVtxp->sourceEdge<0>()->sourcep() == selVtxp->fromp(), selVtxp, - "Operand ordering changed"); - dumpDotEdge(os, *selVtxp->sourceEdge<0>(), ""); - return; - } - } - vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { // if (edge.sourcep()) { string headLabel; @@ -852,14 +829,14 @@ DfgVertex::~DfgVertex() { if (VN_IS(m_dtypep, UnpackArrayDType)) VL_DO_DANGLING(delete m_dtypep, m_dtypep); } -bool DfgVertex::selfEquals(const DfgVertex& that) const { - return this->m_type == that.m_type && this->dtypep() == that.dtypep(); -} +bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; } -V3Hash DfgVertex::selfHash() const { return V3Hash{m_type} + width(); } +V3Hash DfgVertex::selfHash() const { return V3Hash{}; } bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const { if (this == &that) return true; + if (this->type() != that.type()) return false; + if (this->dtypep() != that.dtypep()) return false; if (!this->selfEquals(that)) return false; const auto key = (this < &that) ? EqualsCache::key_type{this, &that} // @@ -893,6 +870,8 @@ V3Hash DfgVertex::hash() { V3Hash& result = user(); if (!result.value()) { V3Hash hash; + hash += m_type; + hash += width(); hash += selfHash(); // Variables are defined by themselves, so there is no need to hash the sources. This // enables sound hashing of graphs circular only through variables, which we rely on. @@ -933,13 +912,25 @@ void DfgVertex::replaceWith(DfgVertex* newSorucep) { // Vertex classes //------------------------------------------------------------------------------ +// DfgConst ---------- + +bool DfgConst::selfEquals(const DfgVertex& that) const { + return constp()->sameTree(that.as()->constp()); +} + +V3Hash DfgConst::selfHash() const { return m_constp->num().toHash(); } + +// DfgSel ---------- + +bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as()->lsb(); } + +V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; } + // DfgVertexVar ---------- bool DfgVertexVar::selfEquals(const DfgVertex& that) const { - if (const DfgVertexVar* otherp = that.cast()) { - UASSERT_OBJ(varp() != otherp->varp(), this, - "There should only be one DfgVarPacked for a given AstVar"); - } + UASSERT_OBJ(varp() != that.as()->varp(), this, + "There should only be one DfgVarPacked for a given AstVar"); return false; } @@ -950,17 +941,6 @@ V3Hash DfgVertexVar::selfHash() const { return hash; } -// DfgConst ---------- - -bool DfgConst::selfEquals(const DfgVertex& that) const { - if (const DfgConst* otherp = that.cast()) { - return constp()->sameTree(otherp->constp()); - } - return false; -} - -V3Hash DfgConst::selfHash() const { return m_constp->num().toHash(); } - //------------------------------------------------------------------------------ // DfgVisitor //------------------------------------------------------------------------------ diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 32344bdff..98d114a67 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -187,15 +187,12 @@ class AstToDfgVisitor final : public VNVisitor { if (AstConcat* const concatp = VN_CAST(nodep, Concat)) { AstNode* const lhsp = concatp->lhsp(); AstNode* const rhsp = concatp->rhsp(); - const uint32_t lWidth = lhsp->width(); - const uint32_t rWidth = rhsp->width(); { FileLine* const lFlp = lhsp->fileline(); DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(lhsp)}; lVtxp->fromp(vtxp); - lVtxp->lsbp(new DfgConst{*m_dfgp, new AstConst{lFlp, rWidth}}); - lVtxp->widthp(new DfgConst{*m_dfgp, new AstConst{lFlp, lWidth}}); + lVtxp->lsb(rhsp->width()); if (!convertAssignment(flp, lhsp, lVtxp)) return false; } @@ -203,8 +200,7 @@ class AstToDfgVisitor final : public VNVisitor { FileLine* const rFlp = rhsp->fileline(); DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(rhsp)}; rVtxp->fromp(vtxp); - rVtxp->lsbp(new DfgConst{*m_dfgp, new AstConst{rFlp, 0u}}); - rVtxp->widthp(new DfgConst{*m_dfgp, new AstConst{rFlp, rWidth}}); + rVtxp->lsb(0); return convertAssignment(flp, rhsp, rVtxp); } } @@ -467,6 +463,36 @@ class AstToDfgVisitor final : public VNVisitor { nodep->user1p(vtxp); } + void visit(AstSel* nodep) override { + UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + if (!VN_IS(nodep->widthp(), Const)) { // This should never be taken, but paranoia + m_foundUnhandled = true; + ++m_ctx.m_nonRepNode; + return; + } + iterate(nodep->fromp()); + if (m_foundUnhandled) return; + + FileLine* const flp = nodep->fileline(); + DfgVertex* vtxp = nullptr; + if (AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) { + DfgSel* const selp = new DfgSel{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)}; + selp->fromp(nodep->fromp()->user1u().to()); + selp->lsb(constp->toUInt()); + vtxp = selp; + } else { + iterate(nodep->lsbp()); + if (m_foundUnhandled) return; + DfgMux* const muxp = new DfgMux{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)}; + muxp->fromp(nodep->fromp()->user1u().to()); + muxp->lsbp(nodep->lsbp()->user1u().to()); + vtxp = muxp; + } + m_uncommittedVertices.push_back(vtxp); + nodep->user1p(vtxp); + } + // The rest of the 'visit' methods are generated by 'astgen' #include "V3Dfg__gen_ast_to_dfg.h" diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index be47278a9..b41660bbf 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -332,6 +332,22 @@ class DfgToAstVisitor final : DfgVisitor { m_resultp = vtxp->constp()->cloneTree(false); } + void visit(DfgSel* vtxp) override { + FileLine* const flp = vtxp->fileline(); + AstNodeMath* const fromp = convertSource(vtxp->fromp()); + AstConst* const lsbp = new AstConst{flp, vtxp->lsb()}; + AstConst* const widthp = new AstConst{flp, vtxp->width()}; + m_resultp = new AstSel{flp, fromp, lsbp, widthp}; + } + + void visit(DfgMux* vtxp) override { + FileLine* const flp = vtxp->fileline(); + AstNodeMath* const fromp = convertSource(vtxp->fromp()); + AstNodeMath* const lsbp = convertSource(vtxp->lsbp()); + AstConst* const widthp = new AstConst{flp, vtxp->width()}; + m_resultp = new AstSel{flp, fromp, lsbp, widthp}; + } + // The rest of the 'visit' methods are generated by 'astgen' #include "V3Dfg__gen_dfg_to_ast.h" diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 05210b43f..40a45dbf7 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -682,6 +682,170 @@ class V3DfgPeephole final : public DfgVisitor { void visit(DfgRedXor* vtxp) override { optimizeReduction(vtxp); } + void visit(DfgSel* vtxp) override { + DfgVertex* const fromp = vtxp->fromp(); + + FileLine* const flp = vtxp->fileline(); + + const uint32_t lsb = vtxp->lsb(); + const uint32_t width = vtxp->width(); + const uint32_t msb = lsb + width - 1; + + if (DfgConst* const constp = fromp->cast()) { + APPLYING(FOLD_SEL) { + DfgConst* const replacementp = makeZero(flp, width); + replacementp->num().opSel(constp->num(), msb, lsb); + vtxp->replaceWith(replacementp); + return; + } + } + + // Full width select, replace with the source. + if (fromp->width() == width) { + UASSERT_OBJ(lsb == 0, fromp, "OOPS"); + APPLYING(REMOVE_FULL_WIDTH_SEL) { + vtxp->replaceWith(fromp); + return; + } + } + + // Sel from Concat + if (DfgConcat* const concatp = fromp->cast()) { + DfgVertex* const lhsp = concatp->lhsp(); + DfgVertex* const rhsp = concatp->rhsp(); + + if (msb < rhsp->width()) { + // If the select is entirely from rhs, then replace with sel from rhs + APPLYING(REMOVE_SEL_FROM_RHS_OF_CONCAT) { // + vtxp->fromp(rhsp); + } + } else if (lsb >= rhsp->width()) { + // If the select is entirely from the lhs, then replace with sel from lhs + APPLYING(REMOVE_SEL_FROM_LHS_OF_CONCAT) { + vtxp->fromp(lhsp); + vtxp->lsb(lsb - rhsp->width()); + } + } else if (lsb == 0 || msb == concatp->width() - 1 // + || lhsp->is() || rhsp->is() // + || !concatp->hasMultipleSinks()) { + // If the select straddles both sides, but at least one of the sides is wholly + // selected, or at least one of the sides is a Const, or this concat has no other + // use, then push the Sel past the Concat + APPLYING(PUSH_SEL_THROUGH_CONCAT) { + const uint32_t rSelWidth = rhsp->width() - lsb; + const uint32_t lSelWidth = width - rSelWidth; + + // The new Lhs vertex + DfgSel* const newLhsp = new DfgSel{m_dfg, flp, dtypeForWidth(lSelWidth)}; + newLhsp->fromp(lhsp); + newLhsp->lsb(0); + + // The new Rhs vertex + DfgSel* const newRhsp = new DfgSel{m_dfg, flp, dtypeForWidth(rSelWidth)}; + newRhsp->fromp(rhsp); + newRhsp->lsb(lsb); + + // The replacement Concat vertex + DfgConcat* const newConcat + = new DfgConcat{m_dfg, concatp->fileline(), vtxp->dtypep()}; + newConcat->lhsp(newLhsp); + newConcat->rhsp(newRhsp); + + // Replace this vertex + vtxp->replaceWith(newConcat); + return; + } + } + } + + if (DfgReplicate* const repp = fromp->cast()) { + // If the Sel is wholly into the source of the Replicate, push the Sel through the + // Replicate and apply it directly to the source of the Replicate. + const uint32_t srcWidth = repp->srcp()->width(); + if (width <= srcWidth) { + const uint32_t newLsb = lsb % srcWidth; + if (newLsb + width <= srcWidth) { + APPLYING(PUSH_SEL_THROUGH_REPLICATE) { + vtxp->fromp(repp->srcp()); + vtxp->lsb(newLsb); + } + } + } + } + + // Sel from Not + if (DfgNot* const notp = fromp->cast()) { + // Replace "Sel from Not" with "Not of Sel" + if (!notp->hasMultipleSinks()) { + UASSERT_OBJ(notp->srcp()->dtypep() == notp->dtypep(), notp, "Mismatched widths"); + APPLYING(PUSH_SEL_THROUGH_NOT) { + // Make Sel select from source of Not + vtxp->fromp(notp->srcp()); + // Add Not after Sel + DfgNot* const replacementp + = new DfgNot{m_dfg, notp->fileline(), vtxp->dtypep()}; + vtxp->replaceWith(replacementp); + replacementp->srcp(vtxp); + } + } + } + + // Sel from Sel + if (DfgSel* const selp = fromp->cast()) { + APPLYING(REPLACE_SEL_FROM_SEL) { + // Make this Sel select from the source of the source Sel + vtxp->fromp(selp->fromp()); + // Adjust LSB + vtxp->lsb(lsb + selp->lsb()); + } + } + + // Sel from Cond + if (DfgCond* const condp = fromp->cast()) { + // If at least one of the branches are a constant, push the select past the cond + if (condp->thenp()->is() || condp->elsep()->is()) { + APPLYING(PUSH_SEL_THROUGH_COND) { + // The new 'then' vertex + DfgSel* const newThenp = new DfgSel{m_dfg, flp, vtxp->dtypep()}; + newThenp->fromp(condp->thenp()); + newThenp->lsb(lsb); + + // The new 'else' vertex + DfgSel* const newElsep = new DfgSel{m_dfg, flp, vtxp->dtypep()}; + newElsep->fromp(condp->elsep()); + newElsep->lsb(lsb); + + // The replacement Cond vertex + DfgCond* const newCondp + = new DfgCond{m_dfg, condp->fileline(), vtxp->dtypep()}; + newCondp->condp(condp->condp()); + newCondp->thenp(newThenp); + newCondp->elsep(newElsep); + + // Replace this vertex + vtxp->replaceWith(newCondp); + return; + } + } + } + + // Sel from ShiftL + if (DfgShiftL* const shiftLp = fromp->cast()) { + // If selecting bottom bits of left shift, push the Sel before the shift + if (lsb == 0) { + UASSERT_OBJ(shiftLp->lhsp()->width() >= width, vtxp, "input of shift narrow"); + APPLYING(PUSH_SEL_THROUGH_SHIFTL) { + vtxp->fromp(shiftLp->lhsp()); + DfgShiftL* const newShiftLp + = new DfgShiftL{m_dfg, shiftLp->fileline(), vtxp->dtypep()}; + vtxp->replaceWith(newShiftLp); + newShiftLp->lhsp(vtxp); + newShiftLp->rhsp(shiftLp->rhsp()); + } + } + } + } + //========================================================================= // DfgVertexBinary - bitwise //========================================================================= @@ -945,20 +1109,14 @@ class V3DfgPeephole final : public DfgVisitor { if (lhsp->isZero()) { DfgConst* const lConstp = lhsp->as(); if (DfgSel* const rSelp = rhsp->cast()) { - if (DfgConst* const rSelLsbConstp = rSelp->lsbp()->cast()) { - if (vtxp->dtypep() == rSelp->fromp()->dtypep() - && rSelLsbConstp->toU32() == lConstp->width()) { - const uint32_t rSelWidth = rSelp->widthp()->as()->toU32(); - UASSERT_OBJ(lConstp->width() + rSelWidth == vtxp->width(), vtxp, - "Inconsistent"); - APPLYING(REPLACE_CONCAT_ZERO_AND_SEL_TOP_WITH_SHIFTR) { - DfgShiftR* const replacementp - = new DfgShiftR{m_dfg, flp, vtxp->dtypep()}; - replacementp->lhsp(rSelp->fromp()); - replacementp->rhsp(makeI32(flp, lConstp->width())); - vtxp->replaceWith(replacementp); - return; - } + if (vtxp->dtypep() == rSelp->fromp()->dtypep() + && rSelp->lsb() == lConstp->width()) { + APPLYING(REPLACE_CONCAT_ZERO_AND_SEL_TOP_WITH_SHIFTR) { + DfgShiftR* const replacementp = new DfgShiftR{m_dfg, flp, vtxp->dtypep()}; + replacementp->lhsp(rSelp->fromp()); + replacementp->rhsp(makeI32(flp, lConstp->width())); + vtxp->replaceWith(replacementp); + return; } } } @@ -967,20 +1125,13 @@ class V3DfgPeephole final : public DfgVisitor { if (rhsp->isZero()) { DfgConst* const rConstp = rhsp->as(); if (DfgSel* const lSelp = lhsp->cast()) { - if (DfgConst* const lSelLsbConstp = lSelp->lsbp()->cast()) { - if (vtxp->dtypep() == lSelp->fromp()->dtypep() - && lSelLsbConstp->toU32() == 0) { - const uint32_t lSelWidth = lSelp->widthp()->as()->toU32(); - UASSERT_OBJ(lSelWidth + rConstp->width() == vtxp->width(), vtxp, - "Inconsistent"); - APPLYING(REPLACE_CONCAT_SEL_BOTTOM_AND_ZERO_WITH_SHIFTL) { - DfgShiftL* const replacementp - = new DfgShiftL{m_dfg, flp, vtxp->dtypep()}; - replacementp->lhsp(lSelp->fromp()); - replacementp->rhsp(makeI32(flp, rConstp->width())); - vtxp->replaceWith(replacementp); - return; - } + if (vtxp->dtypep() == lSelp->fromp()->dtypep() && lSelp->lsb() == 0) { + APPLYING(REPLACE_CONCAT_SEL_BOTTOM_AND_ZERO_WITH_SHIFTL) { + DfgShiftL* const replacementp = new DfgShiftL{m_dfg, flp, vtxp->dtypep()}; + replacementp->lhsp(lSelp->fromp()); + replacementp->rhsp(makeI32(flp, rConstp->width())); + vtxp->replaceWith(replacementp); + return; } } } @@ -1003,22 +1154,14 @@ class V3DfgPeephole final : public DfgVisitor { { const auto joinSels = [this](DfgSel* lSelp, DfgSel* rSelp, FileLine* flp) -> DfgSel* { - DfgConst* const lLsbp = lSelp->lsbp()->cast(); - DfgConst* const lWidthp = lSelp->widthp()->cast(); - DfgConst* const rLsbp = rSelp->lsbp()->cast(); - DfgConst* const rWidthp = rSelp->widthp()->cast(); - if (lLsbp && lWidthp && rLsbp && rWidthp) { - if (lSelp->fromp()->equals(*rSelp->fromp())) { - if (lLsbp->toU32() == rLsbp->toU32() + rWidthp->toU32()) { - // Two consecutive Sels, make a single Sel. - const uint32_t width = lWidthp->toU32() + rWidthp->toU32(); - AstNodeDType* const dtypep = dtypeForWidth(width); - DfgSel* const joinedSelp = new DfgSel{m_dfg, flp, dtypep}; - joinedSelp->fromp(rSelp->fromp()); - joinedSelp->lsbp(rSelp->lsbp()); - joinedSelp->widthp(makeI32(flp, width)); - return joinedSelp; - } + if (lSelp->fromp()->equals(*rSelp->fromp())) { + if (lSelp->lsb() == rSelp->lsb() + rSelp->width()) { + // Two consecutive Sels, make a single Sel. + const uint32_t width = lSelp->width() + rSelp->width(); + DfgSel* const joinedSelp = new DfgSel{m_dfg, flp, dtypeForWidth(width)}; + joinedSelp->fromp(rSelp->fromp()); + joinedSelp->lsb(rSelp->lsb()); + return joinedSelp; } } return nullptr; @@ -1252,187 +1395,6 @@ class V3DfgPeephole final : public DfgVisitor { // DfgVertexTernary //========================================================================= - void visit(DfgSel* vtxp) override { - DfgVertex* const fromp = vtxp->fromp(); - DfgConst* const lsbp = vtxp->lsbp()->cast(); - DfgConst* const widthp = vtxp->widthp()->cast(); - if (!lsbp || !widthp) return; - - FileLine* const flp = vtxp->fileline(); - - UASSERT_OBJ(lsbp->toI32() >= 0, vtxp, "Negative LSB in Sel"); - - const uint32_t lsb = lsbp->toU32(); - const uint32_t width = widthp->toU32(); - const uint32_t msb = lsb + width - 1; - - UASSERT_OBJ(width == vtxp->width(), vtxp, "Incorrect Sel width"); - - if (DfgConst* const constp = fromp->cast()) { - APPLYING(FOLD_SEL) { - DfgConst* const replacementp = makeZero(flp, width); - replacementp->num().opSel(constp->num(), msb, lsb); - vtxp->replaceWith(replacementp); - return; - } - } - - // Full width select, replace with the source. - if (fromp->width() == width) { - UASSERT_OBJ(lsb == 0, fromp, "OOPS"); - APPLYING(REMOVE_FULL_WIDTH_SEL) { - vtxp->replaceWith(fromp); - return; - } - } - - // Sel from Concat - if (DfgConcat* const concatp = fromp->cast()) { - DfgVertex* const lhsp = concatp->lhsp(); - DfgVertex* const rhsp = concatp->rhsp(); - - if (msb < rhsp->width()) { - // If the select is entirely from rhs, then replace with sel from rhs - APPLYING(REMOVE_SEL_FROM_RHS_OF_CONCAT) { // - vtxp->fromp(rhsp); - } - } else if (lsb >= rhsp->width()) { - // If the select is entirely from the lhs, then replace with sel from lhs - APPLYING(REMOVE_SEL_FROM_LHS_OF_CONCAT) { - vtxp->fromp(lhsp); - vtxp->lsbp(makeI32(flp, lsb - rhsp->width())); - } - } else if (lsb == 0 || msb == concatp->width() - 1 // - || lhsp->is() || rhsp->is() // - || !concatp->hasMultipleSinks()) { - // If the select straddles both sides, but at least one of the sides is wholly - // selected, or at least one of the sides is a Const, or this concat has no other - // use, then push the Sel past the Concat - APPLYING(PUSH_SEL_THROUGH_CONCAT) { - const uint32_t rSelWidth = rhsp->width() - lsb; - const uint32_t lSelWidth = width - rSelWidth; - - // The new Lhs vertex - DfgSel* const newLhsp = new DfgSel{m_dfg, flp, dtypeForWidth(lSelWidth)}; - newLhsp->fromp(lhsp); - newLhsp->lsbp(makeI32(lsbp->fileline(), 0)); - newLhsp->widthp(makeI32(widthp->fileline(), lSelWidth)); - - // The new Rhs vertex - DfgSel* const newRhsp = new DfgSel{m_dfg, flp, dtypeForWidth(rSelWidth)}; - newRhsp->fromp(rhsp); - newRhsp->lsbp(makeI32(lsbp->fileline(), lsb)); - newRhsp->widthp(makeI32(widthp->fileline(), rSelWidth)); - - // The replacement Concat vertex - DfgConcat* const newConcat - = new DfgConcat{m_dfg, concatp->fileline(), vtxp->dtypep()}; - newConcat->lhsp(newLhsp); - newConcat->rhsp(newRhsp); - - // Replace this vertex - vtxp->replaceWith(newConcat); - return; - } - } - } - - if (DfgReplicate* const repp = fromp->cast()) { - // If the Sel is wholly into the source of the Replicate, push the Sel through the - // Replicate and apply it directly to the source of the Replicate. - const uint32_t srcWidth = repp->srcp()->width(); - if (width <= srcWidth) { - const uint32_t newLsb = lsb % srcWidth; - if (newLsb + width <= srcWidth) { - APPLYING(PUSH_SEL_THROUGH_REPLICATE) { - vtxp->fromp(repp->srcp()); - vtxp->lsbp(makeI32(flp, newLsb)); - } - } - } - } - - // Sel from Not - if (DfgNot* const notp = fromp->cast()) { - // Replace "Sel from Not" with "Not of Sel" - if (!notp->hasMultipleSinks()) { - UASSERT_OBJ(notp->srcp()->dtypep() == notp->dtypep(), notp, "Mismatched widths"); - APPLYING(PUSH_SEL_THROUGH_NOT) { - // Make Sel select from source of Not - vtxp->fromp(notp->srcp()); - // Add Not after Sel - DfgNot* const replacementp - = new DfgNot{m_dfg, notp->fileline(), vtxp->dtypep()}; - vtxp->replaceWith(replacementp); - replacementp->srcp(vtxp); - } - } - } - - // Sel from Sel - if (DfgSel* const selp = fromp->cast()) { - UASSERT_OBJ(widthp->toU32() <= selp->width(), vtxp, "Out of bound Sel"); - if (DfgConst* const sourceLsbp = selp->lsbp()->cast()) { - UASSERT_OBJ(sourceLsbp->toI32() >= 0, selp, "negative"); - UASSERT_OBJ(selp->widthp()->as()->toU32() >= widthp->toU32(), selp, - "negative"); - APPLYING(REPLACE_SEL_FROM_SEL) { - // Make this Sel select from the source of the source Sel - vtxp->fromp(selp->fromp()); - // Adjust LSB - vtxp->lsbp(makeI32(flp, lsb + sourceLsbp->toU32())); - } - } - } - - // Sel from Cond - if (DfgCond* const condp = fromp->cast()) { - // If at least one of the branches are a constant, push the select past the cond - if (condp->thenp()->is() || condp->elsep()->is()) { - APPLYING(PUSH_SEL_THROUGH_COND) { - // The new 'then' vertex - DfgSel* const newThenp = new DfgSel{m_dfg, flp, vtxp->dtypep()}; - newThenp->fromp(condp->thenp()); - newThenp->lsbp(makeI32(lsbp->fileline(), lsb)); - newThenp->widthp(makeI32(widthp->fileline(), width)); - - // The new 'else' vertex - DfgSel* const newElsep = new DfgSel{m_dfg, flp, vtxp->dtypep()}; - newElsep->fromp(condp->elsep()); - newElsep->lsbp(makeI32(lsbp->fileline(), lsb)); - newElsep->widthp(makeI32(widthp->fileline(), width)); - - // The replacement Cond vertex - DfgCond* const newCondp - = new DfgCond{m_dfg, condp->fileline(), vtxp->dtypep()}; - newCondp->condp(condp->condp()); - newCondp->thenp(newThenp); - newCondp->elsep(newElsep); - - // Replace this vertex - vtxp->replaceWith(newCondp); - return; - } - } - } - - // Sel from ShiftL - if (DfgShiftL* const shiftLp = fromp->cast()) { - // If selecting bottom bits of left shift, push the Sel before the shift - if (lsb == 0) { - UASSERT_OBJ(shiftLp->lhsp()->width() >= width, vtxp, "input of shift narrow"); - APPLYING(PUSH_SEL_THROUGH_SHIFTL) { - vtxp->fromp(shiftLp->lhsp()); - DfgShiftL* const newShiftLp - = new DfgShiftL{m_dfg, shiftLp->fileline(), vtxp->dtypep()}; - vtxp->replaceWith(newShiftLp); - newShiftLp->lhsp(vtxp); - newShiftLp->rhsp(shiftLp->rhsp()); - } - } - } - } - void visit(DfgCond* vtxp) override { UASSERT_OBJ(vtxp->dtypep() == vtxp->thenp()->dtypep(), vtxp, "Width mismatch"); UASSERT_OBJ(vtxp->dtypep() == vtxp->elsep()->dtypep(), vtxp, "Width mismatch"); diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index b9cd763fe..5dccc1c27 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -109,6 +109,47 @@ public: } // 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; + V3Hash selfHash() const override; + +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;