Optimize DFG partial assignments (#6176)
This is mostly a refactoring, but also enables handling some more UNOPTFLAT, when the variable is only partially assigned in the cycle. Previously the way partial assignments to variables were handled were through the DfgVerexVar types themselves, which kept track of all drivers. This has been replaced by DfgVertexSplice (which always drives a DfgVeretexVar), and all DfgVertexVar now only have a single source, either a DfgVertexSplice, if partially assigned, or an arbitrary DfgVertex when updated as a whole.
This commit is contained in:
parent
e2e5d9eaf1
commit
03e0d49d99
195
src/V3Dfg.cpp
195
src/V3Dfg.cpp
|
|
@ -97,6 +97,16 @@ std::unique_ptr<DfgGraph> 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> 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>();
|
||||
DfgVarArray* const cp = vtxp2clonep.at(vp)->as<DfgVarArray>();
|
||||
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>();
|
||||
DfgVarPacked* const cp = vtxp2clonep.at(vp)->as<DfgVarPacked>();
|
||||
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<DfgVertexVar>()->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<DfgVertexVariadic>(), &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<DfgVertexVariadic>()) {
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::atSpliceArray: {
|
||||
const DfgSpliceArray* const vp = vtx.as<DfgSpliceArray>();
|
||||
DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as<DfgSpliceArray>();
|
||||
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>();
|
||||
DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as<DfgSplicePacked>();
|
||||
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<DfgVertexSplice>()) {
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << vtx.typeName() << "\n";
|
||||
if (const DfgSpliceArray* const sp = vtx.cast<DfgSpliceArray>()) {
|
||||
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<DfgVertexVar>()) headLabel = vtx.srcName(idx);
|
||||
if (vtx.arity() > 1 || vtx.is<DfgVertexSplice>()) 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<DfgVertexVar>()) {
|
||||
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<DfgVarArray>(), 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<DfgVarPacked>()) return varp;
|
||||
if (DfgVertexVar* const varp = this->cast<DfgVertexVar>()) 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<DfgVarPacked>();
|
||||
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
|
||||
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<D
|
|||
|
||||
V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; }
|
||||
|
||||
// DfgSpliceArray ----------
|
||||
|
||||
bool DfgSpliceArray::selfEquals(const DfgVertex& that) const {
|
||||
const DfgSpliceArray* const thatp = that.as<DfgSpliceArray>();
|
||||
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<DfgSplicePacked>();
|
||||
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 {
|
||||
|
|
|
|||
16
src/V3Dfg.h
16
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<const DfgVertex*, AstScope*>;
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<DfgVarPacked>()->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<DfgVertexVar>();
|
||||
// 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<DfgVarPacked>()->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<DfgVarArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
||||
return true;
|
||||
return vtxp->srcp()->as<DfgVertexSplice>();
|
||||
}
|
||||
|
||||
++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<DfgSplicePacked>()->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<DfgSpliceArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
||||
return true;
|
||||
}
|
||||
} else if (VN_IS(nodep, VarRef)) {
|
||||
if (DfgVertexSplice* const splicep = convertLValue(nodep)) {
|
||||
splicep->template as<DfgSplicePacked>()->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<DfgSplicePacked>();
|
||||
|
||||
// Gather (and unlink) all drivers
|
||||
std::vector<Driver> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<int>(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<int>(srcp->width()), 0};
|
||||
inverseMask.opNot(independentBits.mask(srcp));
|
||||
result.opSelInto(inverseMask, varp->driverLsb(i), srcp->width());
|
||||
});
|
||||
result.opNot(independentBits.mask(varp->srcp()));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 <typename T_Vertex>
|
||||
void fixSources(T_Vertex& vtx, std::function<void(T_Vertex&, DfgVertex&, size_t)> relink) {
|
||||
static_assert(std::is_base_of<DfgVertexVar, T_Vertex>::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<DfgVertexVar>()) 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<T_Vertex>()), 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<DfgVertexVar>() || 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<DfgVarPacked>()) {
|
||||
fixSources<DfgVarPacked>(
|
||||
*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<DfgVarArray>()) {
|
||||
fixSources<DfgVarArray>( //
|
||||
*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<DfgVarPacked>()) {
|
||||
varp->packSources();
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (DfgVarArray* const varp = vtxp->cast<DfgVarArray>()) {
|
||||
varp->packSources();
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& 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<DfgVertexVar>()) {
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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<DfgSplicePacked>()) {
|
||||
// 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<int>(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<DfgSpliceArray>()) {
|
||||
// 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<DfgVarPacked>()) {
|
||||
convertVarDriver(dfgVarp);
|
||||
convertPackedDriver(dfgVarp);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<DfgVarPacked>();
|
||||
} else {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
varp = dfg.makeNewVar(flp, name, idxDTypep, nullptr)->as<DfgVarPacked>();
|
||||
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<DfgVarPacked>()) {
|
||||
} else if (DfgVarPacked* const driverp = varp->srcp()->cast<DfgVarPacked>()) {
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -1202,11 +1202,15 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
void visit(DfgArraySel* vtxp) override {
|
||||
if (DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>()) {
|
||||
if (DfgVarArray* const varp = vtxp->fromp()->cast<DfgVarArray>()) {
|
||||
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<DfgSpliceArray>()) {
|
||||
const size_t idx = idxp->toSizeT();
|
||||
if (DfgVertex* const driverp = splicep->driverAt(idx)) {
|
||||
APPLYING(INLINE_ARRAYSEL) {
|
||||
replace(vtxp, driverp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<DfgVertexSplice>()) {
|
||||
const bool hasNonVarSink
|
||||
= vtx.findSink<DfgVertex>([](const DfgVertex& snk) { //
|
||||
return !snk.is<DfgVertexVar>();
|
||||
});
|
||||
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<DfgVertexVar>([](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<DfgVertexSplice>()) 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<DfgVarPacked>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<DfgVertexSplice>() && !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<FileLine*, uint32_t>;
|
||||
|
||||
std::vector<DriverData> 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;
|
||||
driverData.swap(m_driverData);
|
||||
|
||||
// Grab and unlink the sources
|
||||
std::vector<DfgVertex*> 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<FileLine*, uint32_t>;
|
||||
|
||||
std::vector<DriverData> m_driverData; // Additional data associate with each driver
|
||||
std::vector<DriverData> 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;
|
||||
driverData.swap(m_driverData);
|
||||
|
||||
// Grab and unlink the sources
|
||||
std::vector<DfgVertex*> 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue