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:
parent
0570cb8d9f
commit
29a080dd9b
|
|
@ -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
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue