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'.
This commit is contained in:
Geza Lore 2022-10-06 18:34:18 +01:00
parent 0570cb8d9f
commit 29a080dd9b
5 changed files with 309 additions and 284 deletions

View File

@ -539,12 +539,6 @@ void DfgGraph::runToFixedPoint(std::function<bool(DfgVertex&)> f) {
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
static bool isSimpleSel(const DfgSel* vtxp) {
const DfgConst* const lp = vtxp->lsbp()->cast<DfgConst>();
const DfgConst* const wp = vtxp->widthp()->cast<DfgConst>();
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<DfgConst>()) {
const bool feedsSimpleSel = !constVtxp->findSink<DfgVertex>([](const DfgVertex& v) { //
return !v.is<DfgSel>() || !isSimpleSel(v.as<DfgSel>());
});
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<DfgSel>()) {
if (isSimpleSel(selVtxp)) {
const uint32_t lsb = selVtxp->lsbp()->as<DfgConst>()->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<const DfgSel>()) {
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<V3Hash>();
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<DfgConst>()->constp());
}
V3Hash DfgConst::selfHash() const { return m_constp->num().toHash(); }
// DfgSel ----------
bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as<DfgSel>()->lsb(); }
V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; }
// DfgVertexVar ----------
bool DfgVertexVar::selfEquals(const DfgVertex& that) const {
if (const DfgVertexVar* otherp = that.cast<DfgVertexVar>()) {
UASSERT_OBJ(varp() != otherp->varp(), this,
"There should only be one DfgVarPacked for a given AstVar");
}
UASSERT_OBJ(varp() != that.as<DfgVertexVar>()->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<DfgConst>()) {
return constp()->sameTree(otherp->constp());
}
return false;
}
V3Hash DfgConst::selfHash() const { return m_constp->num().toHash(); }
//------------------------------------------------------------------------------
// DfgVisitor
//------------------------------------------------------------------------------

View File

@ -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<DfgVertex*>());
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<DfgVertex*>());
muxp->lsbp(nodep->lsbp()->user1u().to<DfgVertex*>());
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"

View File

@ -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"

View File

@ -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<DfgConst>()) {
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<DfgConcat>()) {
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<DfgConst>() || rhsp->is<DfgConst>() //
|| !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<DfgReplicate>()) {
// 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<DfgNot>()) {
// 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<DfgSel>()) {
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<DfgCond>()) {
// If at least one of the branches are a constant, push the select past the cond
if (condp->thenp()->is<DfgConst>() || condp->elsep()->is<DfgConst>()) {
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<DfgShiftL>()) {
// 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<DfgConst>();
if (DfgSel* const rSelp = rhsp->cast<DfgSel>()) {
if (DfgConst* const rSelLsbConstp = rSelp->lsbp()->cast<DfgConst>()) {
if (vtxp->dtypep() == rSelp->fromp()->dtypep()
&& rSelLsbConstp->toU32() == lConstp->width()) {
const uint32_t rSelWidth = rSelp->widthp()->as<DfgConst>()->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<DfgConst>();
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
if (DfgConst* const lSelLsbConstp = lSelp->lsbp()->cast<DfgConst>()) {
if (vtxp->dtypep() == lSelp->fromp()->dtypep()
&& lSelLsbConstp->toU32() == 0) {
const uint32_t lSelWidth = lSelp->widthp()->as<DfgConst>()->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>();
DfgConst* const lWidthp = lSelp->widthp()->cast<DfgConst>();
DfgConst* const rLsbp = rSelp->lsbp()->cast<DfgConst>();
DfgConst* const rWidthp = rSelp->widthp()->cast<DfgConst>();
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>();
DfgConst* const widthp = vtxp->widthp()->cast<DfgConst>();
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<DfgConst>()) {
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<DfgConcat>()) {
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<DfgConst>() || rhsp->is<DfgConst>() //
|| !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<DfgReplicate>()) {
// 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<DfgNot>()) {
// 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<DfgSel>()) {
UASSERT_OBJ(widthp->toU32() <= selp->width(), vtxp, "Out of bound Sel");
if (DfgConst* const sourceLsbp = selp->lsbp()->cast<DfgConst>()) {
UASSERT_OBJ(sourceLsbp->toI32() >= 0, selp, "negative");
UASSERT_OBJ(selp->widthp()->as<DfgConst>()->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<DfgCond>()) {
// If at least one of the branches are a constant, push the select past the cond
if (condp->thenp()->is<DfgConst>() || condp->elsep()->is<DfgConst>()) {
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<DfgShiftL>()) {
// 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");

View File

@ -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;