Refactor DFG IndependentBits analysis to work for any vertex. (#6209)
This used to be restricted to variable vertices, but now can handle arbitrary circular vertices that represent packed values. It also converges faster than the earlier version. Prep for resolving loops through arrays.
This commit is contained in:
parent
7646e7d89c
commit
7401a8a43a
12
src/V3Dfg.h
12
src/V3Dfg.h
|
@ -230,7 +230,13 @@ public:
|
|||
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T>
|
||||
inline T& getUser();
|
||||
inline const T& getUser() const;
|
||||
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T>
|
||||
T& getUser() {
|
||||
return const_cast<T&>(const_cast<const DfgVertex*>(this)->getUser<T>());
|
||||
}
|
||||
|
||||
// Set user data, becomes current.
|
||||
template <typename T>
|
||||
|
@ -783,12 +789,12 @@ T& DfgVertex::user() {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::getUser() {
|
||||
const T& DfgVertex::getUser() const {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const T* const storagep = reinterpret_cast<const T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
|
|
|
@ -580,22 +580,25 @@ public:
|
|||
|
||||
class IndependentBits final : public DfgVisitor {
|
||||
// STATE
|
||||
DfgVarPacked* const m_varp; // The variable we are computing dependencies for
|
||||
std::deque<DfgVertex*> m_workList; // Work list for traversal
|
||||
const uint32_t m_component; // The component the start vertex is part of
|
||||
// Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'.
|
||||
std::unordered_map<const DfgVertex*, V3Number> m_vtxp2Mask;
|
||||
|
||||
std::ofstream m_lineCoverageFile; // Line coverage file, just for testing
|
||||
|
||||
// METHODS
|
||||
|
||||
// Retrieve the mask for the given vertex (create it with value 0 if needed)
|
||||
V3Number& mask(const DfgVertex* vtxp) {
|
||||
return m_vtxp2Mask
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(vtxp), //
|
||||
std::forward_as_tuple(vtxp->fileline(), static_cast<int>(vtxp->width()), 0))
|
||||
.first->second;
|
||||
// Look up (or create) mask for 'vtxp'
|
||||
auto pair = m_vtxp2Mask.emplace(
|
||||
std::piecewise_construct, //
|
||||
std::forward_as_tuple(vtxp), //
|
||||
std::forward_as_tuple(vtxp->fileline(), static_cast<int>(vtxp->width()), 0));
|
||||
// Initialize to all ones if the vertex is part of the same component, otherwise zeroes
|
||||
if (pair.second && vtxp->getUser<uint32_t>() == m_component) {
|
||||
pair.first->second.setAllBits1();
|
||||
}
|
||||
return pair.first->second;
|
||||
}
|
||||
|
||||
// Use this macro to call 'mask' in 'visit' methods. This also emits
|
||||
|
@ -610,8 +613,7 @@ class IndependentBits final : public DfgVisitor {
|
|||
// VISITORS
|
||||
void visit(DfgVertex* vtxp) override {
|
||||
UINFO(9, "Unhandled vertex type " << vtxp->typeName());
|
||||
// Conservatively assume it depends on the variable...
|
||||
mask(vtxp).setAllBits1(); // intentionally not using MASK here
|
||||
// Conservative assumption about all bits being dependent prevails
|
||||
}
|
||||
|
||||
void visit(DfgSplicePacked* vtxp) override {
|
||||
|
@ -624,10 +626,6 @@ 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);
|
||||
}
|
||||
|
||||
|
@ -656,7 +654,10 @@ class IndependentBits final : public DfgVisitor {
|
|||
|
||||
void visit(DfgExtend* vtxp) override {
|
||||
const DfgVertex* const srcp = vtxp->srcp();
|
||||
MASK(vtxp).opSelInto(MASK(srcp), 0, srcp->width());
|
||||
const uint32_t sWidth = srcp->width();
|
||||
V3Number& m = MASK(vtxp);
|
||||
m.opSelInto(MASK(srcp), 0, sWidth);
|
||||
m.opSetRange(sWidth, vtxp->width() - sWidth, '0');
|
||||
}
|
||||
|
||||
void visit(DfgNot* vtxp) override { //
|
||||
|
@ -684,7 +685,9 @@ class IndependentBits final : public DfgVisitor {
|
|||
if (shiftAmount >= width) return;
|
||||
V3Number shiftedMask{lhsp->fileline(), static_cast<int>(width), 0};
|
||||
shiftedMask.opShiftR(MASK(lhsp), rConstp->num());
|
||||
MASK(vtxp).opSelInto(shiftedMask, 0, width - shiftAmount);
|
||||
V3Number& m = MASK(vtxp);
|
||||
m.opSelInto(shiftedMask, 0, width - shiftAmount);
|
||||
m.opSetRange(width - shiftAmount, shiftAmount, '0');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -692,9 +695,9 @@ class IndependentBits final : public DfgVisitor {
|
|||
// the most significant dependent bit might be dependent
|
||||
V3Number& lMask = MASK(lhsp);
|
||||
V3Number& vMask = MASK(vtxp);
|
||||
int idx = width - 1;
|
||||
while (idx >= 0 && lMask.bitIs0(idx)) --idx;
|
||||
while (idx >= 0) vMask.setBit(idx--, '1');
|
||||
uint32_t lzc = 0; // Leading zero count
|
||||
while (lzc < width && lMask.bitIs0(width - 1 - lzc)) ++lzc;
|
||||
while (lzc > 0) vMask.setBit(width - 1 - (--lzc), '0');
|
||||
}
|
||||
|
||||
void visit(DfgShiftL* vtxp) override {
|
||||
|
@ -708,7 +711,9 @@ class IndependentBits final : public DfgVisitor {
|
|||
if (shiftAmount >= width) return;
|
||||
V3Number shiftedMask{lhsp->fileline(), static_cast<int>(width), 0};
|
||||
shiftedMask.opShiftL(MASK(lhsp), rConstp->num());
|
||||
MASK(vtxp).opSelInto(shiftedMask, shiftAmount, width - shiftAmount);
|
||||
V3Number& m = MASK(vtxp);
|
||||
m.opSelInto(shiftedMask, shiftAmount, width - shiftAmount);
|
||||
m.opSetRange(0, shiftAmount, '0');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -716,16 +721,16 @@ class IndependentBits final : public DfgVisitor {
|
|||
// the least significant dependent bit might be dependent
|
||||
V3Number& lMask = MASK(lhsp);
|
||||
V3Number& vMask = MASK(vtxp);
|
||||
uint32_t idx = 0;
|
||||
while (idx < width && lMask.bitIs0(idx)) ++idx;
|
||||
while (idx < width) vMask.setBit(idx++, '1');
|
||||
uint32_t tzc = 0; // Trailing zero count
|
||||
while (tzc < width && lMask.bitIs0(tzc)) ++tzc;
|
||||
while (tzc > 0) vMask.setBit(--tzc, '0');
|
||||
}
|
||||
|
||||
#undef MASK
|
||||
|
||||
// CONSTRUCTOR
|
||||
IndependentBits(DfgVarPacked* varp)
|
||||
: m_varp{varp} {
|
||||
IndependentBits(DfgGraph& dfg, DfgVertex* vtxp)
|
||||
: m_component{vtxp->getUser<uint32_t>()} {
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
m_lineCoverageFile.open( //
|
||||
v3Global.opt.makeDir() + "/" + v3Global.opt.prefix()
|
||||
|
@ -733,21 +738,24 @@ class IndependentBits final : public DfgVisitor {
|
|||
std::ios_base::out | std::ios_base::app);
|
||||
}
|
||||
|
||||
// The starting vertex depends on it's own value, duuhh...
|
||||
mask(varp).setAllBits1();
|
||||
// Enqueue all sinks
|
||||
varp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); });
|
||||
// Work list for the traversal
|
||||
std::deque<DfgVertex*> workList;
|
||||
|
||||
// While there is an item on the worklist ..
|
||||
while (!m_workList.empty()) {
|
||||
// Enqueue every operation vertex in the analysed component
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.getUser<uint32_t>() == m_component) workList.emplace_back(&vtx);
|
||||
}
|
||||
|
||||
// While there is an item on the worklist ...
|
||||
while (!workList.empty()) {
|
||||
// Grab next item
|
||||
DfgVertex* const currp = m_workList.front();
|
||||
m_workList.pop_front();
|
||||
DfgVertex* const currp = workList.front();
|
||||
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); });
|
||||
currp->forEachSink([&](DfgVertex& vtx) { workList.emplace_back(&vtx); });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -761,33 +769,33 @@ class IndependentBits final : public DfgVisitor {
|
|||
|
||||
// If mask changed, enqueue sinks
|
||||
if (!prevMask.isCaseEq(maskCurr)) {
|
||||
currp->forEachSink([&](DfgVertex& vtx) { m_workList.emplace_back(&vtx); });
|
||||
currp->forEachSink([&](DfgVertex& vtx) { workList.emplace_back(&vtx); });
|
||||
|
||||
// Check the mask only ever expands (no bit goes 1 -> 0)
|
||||
// Check the mask only ever contrects (no bit goes 0 -> 1)
|
||||
if (VL_UNLIKELY(v3Global.opt.debugCheck())) {
|
||||
V3Number notCurr{maskCurr};
|
||||
notCurr.opNot(maskCurr);
|
||||
V3Number prevAndNotCurr{maskCurr};
|
||||
prevAndNotCurr.opAnd(prevMask, notCurr);
|
||||
UASSERT_OBJ(prevAndNotCurr.isEqZero(), currp, "Mask should only expand");
|
||||
V3Number notPrev{prevMask};
|
||||
notPrev.opNot(prevMask);
|
||||
V3Number notPrevAndCurr{maskCurr};
|
||||
notPrevAndCurr.opAnd(notPrev, maskCurr);
|
||||
UASSERT_OBJ(notPrevAndCurr.isEqZero(), currp, "Mask should only contract");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// Given a variable, compute which bits in this variable are independent of
|
||||
// the variable itself (simple forward dataflow analysis). Returns a bit
|
||||
// mask where a set bit indicates that bit is independent of the variable
|
||||
// itself (logic is not circular). The result is a conservative estimate,
|
||||
// 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};
|
||||
// Given a Vertex that is part of an SCC denoted by vtxp->user<uint32_t>(),
|
||||
// compute which bits of this vertex have a value that is independent of
|
||||
// the current value of the Vertex itself (simple forward dataflow
|
||||
// analysis). Returns a bit mask where a set bit indicates that bit is
|
||||
// independent of the vertex itself (logic is not circular). The result is
|
||||
// a conservative estimate, so bits reported dependent might not actually
|
||||
// be, but all bits reported independent are known to be so.
|
||||
static V3Number apply(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
IndependentBits independentBits{dfg, vtxp};
|
||||
// The mask represents the dependent bits, so invert it
|
||||
V3Number result{varp->fileline(), static_cast<int>(varp->width()), 0};
|
||||
result.opNot(independentBits.mask(varp->srcp()));
|
||||
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
||||
result.opNot(independentBits.mask(vtxp));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
@ -899,7 +907,7 @@ public:
|
|||
if (usedBits.isEqZero()) return 0;
|
||||
|
||||
// Figure out which bits of 'varp' are dependent on themselves
|
||||
const V3Number indpBits = IndependentBits::apply(varp);
|
||||
const V3Number indpBits = IndependentBits::apply(dfg, varp);
|
||||
UINFO(9, "Independent bits of '" << varp->nodep()->name() << "' are "
|
||||
<< indpBits.displayed(varp->nodep(), "%b"));
|
||||
// Can't do anything if all bits are dependent
|
||||
|
|
|
@ -2242,6 +2242,13 @@ V3Number& V3Number::opBufIf1(const V3Number& ens, const V3Number& if1s) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
// Sets all bits in range to the given value
|
||||
V3Number& V3Number::opSetRange(uint32_t lsb, uint32_t width, char bitValue) {
|
||||
const uint32_t msb = lsb + width - 1;
|
||||
for (uint32_t i = lsb; i <= msb; ++i) setBit(i, bitValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opAssign(const V3Number& lhs) { return opAssignNonXZ(lhs, false); }
|
||||
V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) {
|
||||
// Note may be a width change during the assign.
|
||||
|
|
|
@ -716,6 +716,7 @@ public:
|
|||
V3Number& opWildEq(const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opWildNeq(const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opBufIf1(const V3Number& ens, const V3Number& if1s);
|
||||
V3Number& opSetRange(uint32_t lsb, uint32_t width, char bitValue);
|
||||
// "standard" math
|
||||
V3Number& opNot(const V3Number& lhs);
|
||||
V3Number& opLogNot(const V3Number& lhs);
|
||||
|
|
Loading…
Reference in New Issue