Fix Dfg independent bits analysis performance (#6731) (#6743)

This removes a factor N from DfgBreakCycles, by doing the necessary data
flow analysis for the entire graph up front, and resulting the result for
all subsequent cycle fixups in the current iteration.

Fixes #6731
This commit is contained in:
Geza Lore 2025-11-29 19:01:22 +00:00 committed by GitHub
parent 287d8aef9e
commit 35615c268b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 236 additions and 144 deletions

View File

@ -18,12 +18,12 @@
#include "V3Dfg.h" #include "V3Dfg.h"
#include "V3DfgPasses.h" #include "V3DfgPasses.h"
#include "V3Error.h"
#include "V3Hash.h" #include "V3Hash.h"
#include <algorithm> #include <algorithm>
#include <deque> #include <deque>
#include <fstream> #include <fstream>
#include <limits>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -264,6 +264,18 @@ class TraceDriver final : public DfgVisitor {
return nullptr; return nullptr;
} }
template <typename Vertex>
Vertex* traceReduction(Vertex* vtxp) {
static_assert(std::is_base_of<DfgVertexUnary, Vertex>::value,
"Should only call on DfgVertexUnary");
if (DfgVertex* const sp = trace(vtxp->srcp(), vtxp->srcp()->width() - 1, 0)) {
Vertex* const resp = make<Vertex>(vtxp, 1);
resp->srcp(sp);
return resp;
}
return nullptr;
}
template <typename Vertex> template <typename Vertex>
Vertex* traceCmp(Vertex* vtxp) { Vertex* traceCmp(Vertex* vtxp) {
static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value, static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value,
@ -315,10 +327,10 @@ class TraceDriver final : public DfgVisitor {
#endif #endif
// VISITORS // VISITORS
void visit(DfgVertex* vtxp) override { void visit(DfgVertex* vtxp) override { // LCOV_EXCL_START
// Base case: cannot continue ... // Base case: cannot continue ...
UINFO(9, "TraceDriver - Unhandled vertex type: " << vtxp->typeName()); UINFO(9, "TraceDriver - Unhandled vertex type: " << vtxp->typeName());
} } // LCOV_EXCL_STOP
void visit(DfgSplicePacked* vtxp) override { void visit(DfgSplicePacked* vtxp) override {
struct Driver final { struct Driver final {
@ -567,6 +579,18 @@ class TraceDriver final : public DfgVisitor {
if (!m_aggressive) return; if (!m_aggressive) return;
SET_RESULT(traceAddSub(vtxp)); SET_RESULT(traceAddSub(vtxp));
} }
void visit(DfgRedAnd* vtxp) override {
if (!m_aggressive) return;
SET_RESULT(traceReduction(vtxp));
}
void visit(DfgRedOr* vtxp) override {
if (!m_aggressive) return;
SET_RESULT(traceReduction(vtxp));
}
void visit(DfgRedXor* vtxp) override {
if (!m_aggressive) return;
SET_RESULT(traceReduction(vtxp));
}
void visit(DfgEq* vtxp) override { void visit(DfgEq* vtxp) override {
if (!m_aggressive) return; if (!m_aggressive) return;
SET_RESULT(traceCmp(vtxp)); SET_RESULT(traceCmp(vtxp));
@ -730,23 +754,29 @@ public:
class IndependentBits final : public DfgVisitor { class IndependentBits final : public DfgVisitor {
// STATE // STATE
const Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map const Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map
const uint64_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 are independent of the SCC
// 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::unordered_map<const DfgVertex*, V3Number> m_vtxp2Mask;
std::deque<DfgVertex*> m_workList; // Work list for the traversal
std::unordered_map<DfgVertex*, bool> m_onWorkList; // Marks vertices on the work list
std::ofstream m_lineCoverageFile; // Line coverage file, just for testing std::ofstream m_lineCoverageFile; // Line coverage file, just for testing
// METHODS // METHODS
// Predicate to check if a vertex should be analysed directly
bool handledDirectly(const DfgVertex& vtx) const {
if (!vtx.isPacked()) return false;
if (vtx.is<DfgSplicePacked>()) return false;
return true;
}
// Retrieve the mask for the given vertex (create it with value 0 if needed) // Retrieve the mask for the given vertex (create it with value 0 if needed)
V3Number& mask(const DfgVertex* vtxp) { V3Number& mask(const DfgVertex& vtx) {
UASSERT_OBJ(handledDirectly(vtx), &vtx, "Vertex should not be handled direclty");
// Look up (or create) mask for 'vtxp' // Look up (or create) mask for 'vtxp'
auto pair = m_vtxp2Mask.emplace( return m_vtxp2Mask
std::piecewise_construct, // .emplace(std::piecewise_construct, //
std::forward_as_tuple(vtxp), // std::forward_as_tuple(&vtx), //
std::forward_as_tuple(vtxp->fileline(), static_cast<int>(vtxp->width()), 0)); std::forward_as_tuple(vtx.fileline(), static_cast<int>(vtx.width()), 0)) //
// Initialize to all ones if the vertex is part of the same component, otherwise zeroes .first->second;
if (pair.second && m_vtx2Scc.at(vtxp) == m_component) { pair.first->second.setAllBits1(); }
return pair.first->second;
} }
// Use this macro to call 'mask' in 'visit' methods. This also emits // Use this macro to call 'mask' in 'visit' methods. This also emits
@ -754,56 +784,36 @@ class IndependentBits final : public DfgVisitor {
// TODO: Use C++20 std::source_location instead of a macro // TODO: Use C++20 std::source_location instead of a macro
#ifdef VL_DEBUG #ifdef VL_DEBUG
#define MASK(vtxp) \ #define MASK(vtxp) \
([this](const DfgVertex* p) -> V3Number& { \ ([this](const DfgVertex& vtx) -> V3Number& { \
if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \ if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \
return mask(p); \ return mask(vtx); \
}(vtxp)) }(*vtxp))
#else #else
#define MASK(vtxp) mask(vtxp) #define MASK(vtxp) mask(*vtxp)
#endif #endif
// Set all bits at or below the most signicant set bit // Clear all bits at or below the most significant clear bit
void floodTowardsLsb(V3Number& num) { void floodTowardsLsb(V3Number& num) {
bool set = false;
for (int i = num.width() - 1; i >= 0; --i) { for (int i = num.width() - 1; i >= 0; --i) {
if (num.bitIs1(i)) set = true; if (num.bitIs1(i)) continue;
if (set) num.setBit(i, '1'); num.opSetRange(0, i + 1, '0');
break;
} }
} }
// Set all bits at or above the least signicant set bit // Clear all bits at or above the least significant clear bit
void floodTowardsMsb(V3Number& num) { void floodTowardsMsb(V3Number& num) {
bool set = false;
for (int i = 0; i < num.width(); ++i) { for (int i = 0; i < num.width(); ++i) {
if (num.bitIs1(i)) set = true; if (num.bitIs1(i)) continue;
if (set) num.setBit(i, '1'); num.opSetRange(i, num.width() - i, '0');
break;
} }
} }
// VISITORS void propagateFromDriver(V3Number& m, DfgVertex* srcp) {
void visit(DfgVertex* vtxp) override { // If there is no driver, we are done
UINFO(9, "IndependentBits - Unhandled vertex type: " << vtxp->typeName());
// Conservative assumption about all bits being dependent prevails
}
void visit(DfgSplicePacked* vtxp) override {
// Combine the masks of all drivers
V3Number& m = MASK(vtxp);
vtxp->foreachDriver([&](DfgVertex& src, uint32_t lo) {
m.opSelInto(MASK(&src), lo, src.width());
return false;
});
}
void visit(DfgVarPacked* vtxp) override {
DfgVertex* const srcp = vtxp->srcp();
if (srcp && srcp->is<DfgSpliceArray>()) return;
V3Number& m = MASK(vtxp);
DfgVertex* const defaultp = vtxp->defaultp();
if (defaultp) m = MASK(defaultp);
if (!srcp) return; if (!srcp) return;
// If it is driven by a splice, we need to combine the masks of the drivers
if (DfgSplicePacked* const splicep = srcp->cast<DfgSplicePacked>()) { if (DfgSplicePacked* const splicep = srcp->cast<DfgSplicePacked>()) {
splicep->foreachDriver([&](DfgVertex& src, uint32_t lo) { splicep->foreachDriver([&](DfgVertex& src, uint32_t lo) {
m.opSelInto(MASK(&src), lo, src.width()); m.opSelInto(MASK(&src), lo, src.width());
@ -811,10 +821,27 @@ class IndependentBits final : public DfgVisitor {
}); });
return; return;
} }
// Otherwise, we just use the mask of the single driver
m = MASK(srcp); m = MASK(srcp);
} }
// VISITORS
void visit(DfgVertex* vtxp) override { // LCOV_EXCL_START
UASSERT_OBJ(handledDirectly(*vtxp), vtxp, "Vertex should be handled direclty");
UINFO(9, "IndependentBits - Unhandled vertex type: " << vtxp->typeName());
// Conservative assumption about all bits being dependent prevails
} // LCOV_EXCL_STOP
void visit(DfgVarPacked* vtxp) override {
V3Number& m = MASK(vtxp);
DfgVertex* const srcp = vtxp->srcp();
DfgVertex* const defaultp = vtxp->defaultp();
// If there is a default driver, we start from that
if (defaultp) m = MASK(defaultp);
// Then propagate mask from the driver
propagateFromDriver(m, srcp);
}
void visit(DfgArraySel* vtxp) override { void visit(DfgArraySel* vtxp) override {
// Only constant select // Only constant select
const DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>(); const DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>();
@ -834,8 +861,8 @@ class IndependentBits final : public DfgVisitor {
if (!driverp) return; if (!driverp) return;
const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>(); const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>();
if (!uap) return; if (!uap) return;
// Update mask // Propagate from driver
MASK(vtxp) = MASK(uap->srcp()); propagateFromDriver(MASK(vtxp), uap->srcp());
} }
void visit(DfgConcat* vtxp) override { void visit(DfgConcat* vtxp) override {
@ -864,11 +891,11 @@ class IndependentBits final : public DfgVisitor {
void visit(DfgExtend* vtxp) override { void visit(DfgExtend* vtxp) override {
const DfgVertex* const srcp = vtxp->srcp(); const DfgVertex* const srcp = vtxp->srcp();
const uint32_t sWidth = srcp->width(); const uint32_t sWidth = srcp->width();
V3Number& s = MASK(srcp);
V3Number& m = MASK(vtxp); V3Number& m = MASK(vtxp);
m.opSelInto(MASK(srcp), 0, sWidth); m.opSelInto(s, 0, sWidth);
m.opSetRange(sWidth, vtxp->width() - sWidth, '0'); m.opSetRange(sWidth, vtxp->width() - sWidth, '1');
} }
void visit(DfgExtendS* vtxp) override { void visit(DfgExtendS* vtxp) override {
const DfgVertex* const srcp = vtxp->srcp(); const DfgVertex* const srcp = vtxp->srcp();
const uint32_t sWidth = srcp->width(); const uint32_t sWidth = srcp->width();
@ -883,49 +910,71 @@ class IndependentBits final : public DfgVisitor {
} }
void visit(DfgAnd* vtxp) override { // void visit(DfgAnd* vtxp) override { //
MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); MASK(vtxp).opAnd(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
} }
void visit(DfgOr* vtxp) override { // void visit(DfgOr* vtxp) override { //
MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); MASK(vtxp).opAnd(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
} }
void visit(DfgXor* vtxp) override { // void visit(DfgXor* vtxp) override { //
MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); MASK(vtxp).opAnd(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
} }
void visit(DfgAdd* vtxp) override { void visit(DfgAdd* vtxp) override {
V3Number& m = MASK(vtxp); V3Number& m = MASK(vtxp);
m.opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); m.opAnd(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
floodTowardsMsb(m); floodTowardsMsb(m);
} }
void visit(DfgSub* vtxp) override { // Same as Add: 2's complement (a - b) == (a + ~b + 1) void visit(DfgSub* vtxp) override { // Same as Add: 2's complement (a - b) == (a + ~b + 1)
V3Number& m = MASK(vtxp); V3Number& m = MASK(vtxp);
m.opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp())); m.opAnd(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
floodTowardsMsb(m); floodTowardsMsb(m);
} }
void visit(DfgRedAnd* vtxp) override { //
if (MASK(vtxp->lhsp()).isEqAllOnes()) { //
MASK(vtxp).setAllBits1();
}
}
void visit(DfgRedOr* vtxp) override { //
if (MASK(vtxp->lhsp()).isEqAllOnes()) { //
MASK(vtxp).setAllBits1();
}
}
void visit(DfgRedXor* vtxp) override { //
if (MASK(vtxp->lhsp()).isEqAllOnes()) { //
MASK(vtxp).setAllBits1();
}
}
void visit(DfgEq* vtxp) override { void visit(DfgEq* vtxp) override {
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); const bool independent
MASK(vtxp).setBit(0, independent ? '0' : '1'); = MASK(vtxp->lhsp()).isEqAllOnes() && MASK(vtxp->rhsp()).isEqAllOnes();
MASK(vtxp).setBit(0, independent ? '1' : '0');
} }
void visit(DfgNeq* vtxp) override { void visit(DfgNeq* vtxp) override {
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); const bool independent
MASK(vtxp).setBit(0, independent ? '0' : '1'); = MASK(vtxp->lhsp()).isEqAllOnes() && MASK(vtxp->rhsp()).isEqAllOnes();
MASK(vtxp).setBit(0, independent ? '1' : '0');
} }
void visit(DfgLt* vtxp) override { void visit(DfgLt* vtxp) override {
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); const bool independent
MASK(vtxp).setBit(0, independent ? '0' : '1'); = MASK(vtxp->lhsp()).isEqAllOnes() && MASK(vtxp->rhsp()).isEqAllOnes();
MASK(vtxp).setBit(0, independent ? '1' : '0');
} }
void visit(DfgLte* vtxp) override { void visit(DfgLte* vtxp) override {
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); const bool independent
MASK(vtxp).setBit(0, independent ? '0' : '1'); = MASK(vtxp->lhsp()).isEqAllOnes() && MASK(vtxp->rhsp()).isEqAllOnes();
MASK(vtxp).setBit(0, independent ? '1' : '0');
} }
void visit(DfgGt* vtxp) override { void visit(DfgGt* vtxp) override {
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); const bool independent
MASK(vtxp).setBit(0, independent ? '0' : '1'); = MASK(vtxp->lhsp()).isEqAllOnes() && MASK(vtxp->rhsp()).isEqAllOnes();
MASK(vtxp).setBit(0, independent ? '1' : '0');
} }
void visit(DfgGte* vtxp) override { void visit(DfgGte* vtxp) override {
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero(); const bool independent
MASK(vtxp).setBit(0, independent ? '0' : '1'); = MASK(vtxp->lhsp()).isEqAllOnes() && MASK(vtxp->rhsp()).isEqAllOnes();
MASK(vtxp).setBit(0, independent ? '1' : '0');
} }
void visit(DfgShiftR* vtxp) override { void visit(DfgShiftR* vtxp) override {
@ -938,9 +987,10 @@ class IndependentBits final : public DfgVisitor {
const uint32_t shiftAmount = rConstp->toU32(); const uint32_t shiftAmount = rConstp->toU32();
V3Number& m = MASK(vtxp); V3Number& m = MASK(vtxp);
if (shiftAmount >= width) { if (shiftAmount >= width) {
m.setAllBits0(); m.setAllBits1();
} else { } else {
m.opShiftR(MASK(lhsp), rConstp->num()); m.opShiftR(MASK(lhsp), rConstp->num());
m.opSetRange(width - shiftAmount, shiftAmount, '1');
} }
return; return;
} }
@ -962,9 +1012,10 @@ class IndependentBits final : public DfgVisitor {
const uint32_t shiftAmount = rConstp->toU32(); const uint32_t shiftAmount = rConstp->toU32();
V3Number& m = MASK(vtxp); V3Number& m = MASK(vtxp);
if (shiftAmount >= width) { if (shiftAmount >= width) {
m.setAllBits0(); m.setAllBits1();
} else { } else {
m.opShiftL(MASK(lhsp), rConstp->num()); m.opShiftL(MASK(lhsp), rConstp->num());
m.opSetRange(0, shiftAmount, '1');
} }
return; return;
} }
@ -977,19 +1028,33 @@ class IndependentBits final : public DfgVisitor {
} }
void visit(DfgCond* vtxp) override { void visit(DfgCond* vtxp) override {
if (!MASK(vtxp->condp()).isEqZero()) { if (MASK(vtxp->condp()).isEqAllOnes()) {
MASK(vtxp).setAllBits1(); MASK(vtxp).opAnd(MASK(vtxp->thenp()), MASK(vtxp->elsep()));
} else {
MASK(vtxp).opOr(MASK(vtxp->thenp()), MASK(vtxp->elsep()));
} }
} }
#undef MASK #undef MASK
// CONSTRUCTOR // Enqueue sinks of vertex to work list for traversal - only called from constructor
IndependentBits(DfgGraph& dfg, const Vtx2Scc& vtx2Scc, DfgVertex& vertex) void enqueueSinks(DfgVertex& vtx) {
: m_vtx2Scc{vtx2Scc} vtx.foreachSink([&](DfgVertex& sink) {
, m_component{m_vtx2Scc.at(vertex)} { // Ignore if sink is not part of an SCC, already has all bits marked independent
if (!m_vtx2Scc.at(sink)) return false;
// If a vertex is not handled directly, recursively enqueue its sinks instead
if (!handledDirectly(sink)) {
enqueueSinks(sink);
return false;
}
// Otherwise just enqueue it
bool& onWorkList = m_onWorkList[&sink];
if (!onWorkList) m_workList.emplace_back(&sink);
onWorkList = true;
return false;
});
};
IndependentBits(DfgGraph& dfg, const Vtx2Scc& vtx2Scc)
: m_vtx2Scc{vtx2Scc} {
#ifdef VL_DEBUG #ifdef VL_DEBUG
if (v3Global.opt.debugCheck()) { if (v3Global.opt.debugCheck()) {
@ -1000,71 +1065,71 @@ class IndependentBits final : public DfgVisitor {
} }
#endif #endif
// Work list for the traversal // Set up the initial conditions:
std::deque<DfgVertex*> workList; // - For vertices not in an SCC, mark all bits as independent
// - For vertices in an SCC, dispatch to analyse at least once, as some vertices
// always assign constants bits, which are always independent (eg Extend/Shift)
// Enqueue sinks of all SCC vertices that have at least one independent bit
dfg.forEachVertex([&](DfgVertex& vtx) {
if (!handledDirectly(vtx)) return;
if (m_vtx2Scc.at(&vtx)) return;
mask(vtx).setAllBits1();
});
dfg.forEachVertex([&](DfgVertex& vtx) {
if (!handledDirectly(vtx)) return;
if (!m_vtx2Scc.at(&vtx)) return;
iterate(&vtx);
if (!mask(vtx).isEqZero()) enqueueSinks(vtx);
UINFO(9, "Initial independent bits of " << &vtx << " " << vtx.typeName() << " are: "
<< mask(vtx).displayed(vtx.fileline(), "%b"));
});
// Enqueue every operation vertex in the analysed component // Propagate independent bits until no more changes are made
for (DfgVertex& vtx : dfg.opVertices()) { while (!m_workList.empty()) {
if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx);
}
// While there is an item on the worklist ...
while (!workList.empty()) {
// Grab next item // Grab next item
DfgVertex* const currp = workList.front(); DfgVertex* const currp = m_workList.front();
workList.pop_front(); m_workList.pop_front();
m_onWorkList[currp] = false;
if (currp->isArray()) { // Should not enqueue vertices that are not in an SCC
// For an unpacked array vertex, just enque it's sinks. UASSERT_OBJ(m_vtx2Scc.at(currp), currp, "Vertex should be in an SCC");
// (There can be no loops through arrays directly) // Should only enqueue packed vertices
currp->foreachSink([&](DfgVertex& vtx) { UASSERT_OBJ(handledDirectly(*currp), currp, "Vertex should be handled directly");
if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx);
return false;
});
continue;
}
// Grab current mask of item // Grab current mask of item
const V3Number& maskCurr = mask(currp); const V3Number& currMask = mask(*currp);
// Remember current mask // Remember current mask so we can check if it changed
const V3Number prevMask = maskCurr; const V3Number prevMask = currMask;
// Dispatch // Dispatch it to update the mask in the visit methods
iterate(currp); iterate(currp);
// If mask changed, enqueue sinks // If mask changed, enqueue sinks
if (!prevMask.isCaseEq(maskCurr)) { if (!prevMask.isCaseEq(currMask)) {
currp->foreachSink([&](DfgVertex& vtx) { enqueueSinks(*currp);
if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx); // Check the mask only ever expands (no bit goes 1 -> 0)
return false;
});
// Check the mask only ever contrects (no bit goes 0 -> 1)
if (VL_UNLIKELY(v3Global.opt.debugCheck())) { if (VL_UNLIKELY(v3Global.opt.debugCheck())) {
V3Number notPrev{prevMask}; V3Number notCurr{currMask};
notPrev.opNot(prevMask); notCurr.opNot(currMask);
V3Number notPrevAndCurr{maskCurr}; V3Number prevAndNotCurr{currMask};
notPrevAndCurr.opAnd(notPrev, maskCurr); prevAndNotCurr.opAnd(prevMask, notCurr);
UASSERT_OBJ(notPrevAndCurr.isEqZero(), currp, "Mask should only contract"); UASSERT_OBJ(prevAndNotCurr.isEqZero(), currp, "Mask should only expand");
} }
UINFO(9, "Independent bits of " //
<< currp << " " << currp->typeName() << " changed" //
<< "\n form: " << prevMask.displayed(currp->fileline(), "%b")
<< "\n to: " << currMask.displayed(currp->fileline(), "%b"));
} }
} }
} }
public: public:
// Given a Vertex that is part of an SCC denoted by vtxp->user<uint64_t>(), // Given a DfgGraph, and a map from vertices to the SCCs they reside in,
// compute which bits of this vertex have a value that is independent of // returns a map from vertices to a bit mask, where a bit in the mask is
// the current value of the Vertex itself (simple forward dataflow // set if the corresponding bit in that vertex is known to be independent
// analysis). Returns a bit mask where a set bit indicates that bit is // of the values of vertices in the same SCC as the vertex resides in.
// independent of the vertex itself (logic is not circular). The result is static std::unordered_map<const DfgVertex*, V3Number> apply(DfgGraph& dfg,
// a conservative estimate, so bits reported dependent might not actually const Vtx2Scc& vtx2Scc) {
// be, but all bits reported independent are known to be so. return std::move(IndependentBits{dfg, vtx2Scc}.m_vtxp2Mask);
static V3Number apply(DfgGraph& dfg, const Vtx2Scc& vtx2Scc, DfgVertex& vtx) {
IndependentBits independentBits{dfg, vtx2Scc, vtx};
// The mask represents the dependent bits, so invert it
V3Number result{vtx.fileline(), static_cast<int>(vtx.width()), 0};
result.opNot(independentBits.mask(&vtx));
return result;
} }
}; };
@ -1146,6 +1211,8 @@ public:
class FixUpIndependentRanges final { class FixUpIndependentRanges final {
DfgGraph& m_dfg; // The graph being processed DfgGraph& m_dfg; // The graph being processed
Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map
// The independent bits map
const std::unordered_map<const DfgVertex*, V3Number>& m_independentBits;
size_t m_nImprovements = 0; // Number of improvements mde size_t m_nImprovements = 0; // Number of improvements mde
// Returns a bitmask set if that bit of 'vtx' is used (has a sink) // Returns a bitmask set if that bit of 'vtx' is used (has a sink)
@ -1219,6 +1286,14 @@ class FixUpIndependentRanges final {
void fixUpPacked(DfgVertex& vtx) { void fixUpPacked(DfgVertex& vtx) {
UASSERT_OBJ(vtx.isPacked(), &vtx, "Should be a packed type"); UASSERT_OBJ(vtx.isPacked(), &vtx, "Should be a packed type");
UASSERT_OBJ(!vtx.is<DfgSplicePacked>(), &vtx, "Should not be a splice");
// Get which bits of 'vtxp' are independent of the SCCs
const V3Number& indpBits = m_independentBits.at(&vtx);
UINFO(9, "Independent bits of '" << debugStr(vtx) << "' are "
<< indpBits.displayed(vtx.fileline(), "%b"));
// Can't do anything if all bits are dependent
if (indpBits.isEqZero()) return;
// Figure out which bits of 'vtxp' are used // Figure out which bits of 'vtxp' are used
const V3Number usedBits = computeUsedBits(vtx); const V3Number usedBits = computeUsedBits(vtx);
@ -1227,13 +1302,6 @@ class FixUpIndependentRanges final {
// Nothing to do if no bits are used // Nothing to do if no bits are used
if (usedBits.isEqZero()) return; if (usedBits.isEqZero()) return;
// Figure out which bits of 'vtxp' are dependent of themselves
const V3Number indpBits = IndependentBits::apply(m_dfg, m_vtx2Scc, vtx);
UINFO(9, "Independent bits of '" << debugStr(vtx) << "' are "
<< indpBits.displayed(vtx.fileline(), "%b"));
// Can't do anything if all bits are dependent
if (indpBits.isEqZero()) return;
{ {
// Nothing to do if no used bits are independen (all used bits are dependent) // Nothing to do if no used bits are independen (all used bits are dependent)
V3Number usedAndIndependent{vtx.fileline(), static_cast<int>(vtx.width()), 0}; V3Number usedAndIndependent{vtx.fileline(), static_cast<int>(vtx.width()), 0};
@ -1331,9 +1399,12 @@ class FixUpIndependentRanges final {
UINFO(9, "FixUpIndependentRanges made " << m_nImprovements << " improvements"); UINFO(9, "FixUpIndependentRanges made " << m_nImprovements << " improvements");
} }
FixUpIndependentRanges(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) FixUpIndependentRanges(DfgGraph& dfg, Vtx2Scc& vtx2Scc,
const std::unordered_map<const DfgVertex*, V3Number>& independentBits,
DfgVertexVar& var)
: m_dfg{dfg} : m_dfg{dfg}
, m_vtx2Scc{vtx2Scc} { , m_vtx2Scc{vtx2Scc}
, m_independentBits{independentBits} {
main(var); main(var);
} }
@ -1341,8 +1412,10 @@ public:
// Similar to FixUpSelDrivers, but first comptute which bits of the // Similar to FixUpSelDrivers, but first comptute which bits of the
// variable are self dependent, and fix up those that are independent // variable are self dependent, and fix up those that are independent
// but used. // but used.
static size_t apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) { static size_t apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc,
return FixUpIndependentRanges{dfg, vtx2Scc, var}.m_nImprovements; const std::unordered_map<const DfgVertex*, V3Number>& independentBits,
DfgVertexVar& var) {
return FixUpIndependentRanges{dfg, vtx2Scc, independentBits, var}.m_nImprovements;
} }
}; };
@ -1415,11 +1488,15 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
if (nImprovements != prevNImprovements) continue; if (nImprovements != prevNImprovements) continue;
// Method 2. FixUpIndependentRanges // Method 2. FixUpIndependentRanges
const std::unordered_map<const DfgVertex*, V3Number> independentBits
= IndependentBits::apply(res, vtx2Scc);
for (DfgVertexVar& vtx : res.varVertices()) { for (DfgVertexVar& vtx : res.varVertices()) {
// If Variable is not part of a cycle, move on // If Variable is not part of a cycle, move on
if (!vtx2Scc[vtx]) continue; if (!vtx2Scc[vtx]) continue;
const size_t nFixed = FixUpIndependentRanges::apply(res, vtx2Scc, vtx); const size_t nFixed
= FixUpIndependentRanges::apply(res, vtx2Scc, independentBits, vtx);
if (nFixed) { if (nFixed) {
nImprovements += nFixed; nImprovements += nFixed;
ctx.m_breakCyclesContext.m_nImprovements += nFixed; ctx.m_breakCyclesContext.m_nImprovements += nFixed;

View File

@ -167,6 +167,21 @@ module t (
assign SUB_B = (SUB_A << 4) - rand_a[7:0]; assign SUB_B = (SUB_A << 4) - rand_a[7:0];
assign SUB_A = {SUB_C[7], 7'd0}; assign SUB_A = {SUB_C[7], 7'd0};
`signal(REDAND_A, 1); // UNOPTFLAT
`signal(REDAND_B, 3);
assign REDAND_A = &(REDAND_B >> 1);
assign REDAND_B = {rand_a[1:0], REDAND_A};
`signal(REDOR_A, 1); // UNOPTFLAT
`signal(REDOR_B, 3);
assign REDOR_A = |(REDOR_B >> 1);
assign REDOR_B = {rand_a[1:0], REDOR_A};
`signal(REDXOR_A, 1); // UNOPTFLAT
`signal(REDXOR_B, 3);
assign REDXOR_A = ^(REDXOR_B >> 1);
assign REDXOR_B = {rand_a[1:0], REDXOR_A};
`signal(EQ_A, 1); // UNOPTFLAT `signal(EQ_A, 1); // UNOPTFLAT
`signal(EQ_B, 3); `signal(EQ_B, 3);
assign EQ_A = EQ_B >> 1 == rand_b[2:0]; assign EQ_A = EQ_B >> 1 == rand_b[2:0];