Dfg: Use a worklist driven algorithm for unused vertex removal

This improves verilation speed slightly.
This commit is contained in:
Geza Lore 2022-11-06 14:13:42 +00:00
parent fb9ec03c3f
commit 454efbe3fc

View File

@ -209,29 +209,53 @@ void V3DfgPasses::removeVars(DfgGraph& dfg, DfgRemoveVarsContext& ctx) {
} }
void V3DfgPasses::removeUnused(DfgGraph& dfg) { void V3DfgPasses::removeUnused(DfgGraph& dfg) {
// Iteratively remove operation vertices // DfgVertex::user is the next pointer of the work list elements
while (true) { const auto userDataInUse = dfg.userDataInUse();
// Do one pass over the graph.
bool changed = false; // Head of work list. Note that we want all next pointers in the list to be non-zero (including
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { // that of the last element). This allows as to do two important things: detect if an element
nextp = vtxp->verticesNext(); // is in the list by checking for a non-zero next poitner, and easy prefetching without
if (!vtxp->hasSinks()) { // conditionals. The address of the graph is a good sentinel as it is a valid memory address,
changed = true; // and we can easily check for the end of the list.
vtxp->unlinkDelete(dfg); DfgVertex* const sentinelp = reinterpret_cast<DfgVertex*>(&dfg);
} DfgVertex* workListp = sentinelp;
// Add all unused vertices to the work list. This also allocates all DfgVertex::user.
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
nextp = vtxp->verticesNext();
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
if (vtxp->hasSinks()) {
// This vertex is used. Allocate user, but don't add to work list.
vtxp->setUser<DfgVertex*>(nullptr);
} else {
// This vertex is unused. Add to work list.
vtxp->setUser<DfgVertex*>(workListp);
workListp = vtxp;
} }
if (!changed) break; }
// Do another pass in the opposite direction. Alternating directions reduces
// the pathological complexity with left/right leaning trees. // Process the work list
changed = false; while (workListp != sentinelp) {
for (DfgVertex *vtxp = dfg.opVerticesRbeginp(), *nextp; vtxp; vtxp = nextp) { // Pick up the head
nextp = vtxp->verticesPrev(); DfgVertex* const vtxp = workListp;
if (!vtxp->hasSinks()) { // Detach the head
changed = true; workListp = vtxp->getUser<DfgVertex*>();
vtxp->unlinkDelete(dfg); // Prefetch next item
} VL_PREFETCH_RW(workListp);
} // If used, then nothing to do, so move on
if (!changed) break; if (vtxp->hasSinks()) continue;
// Add sources of unused vertex to work list
vtxp->forEachSource([&](DfgVertex& src) {
// We only remove actual operation vertices in this loop
if (src.is<DfgConst>() || src.is<DfgVertexVar>()) return;
// If already in work list then nothing to do
if (src.getUser<DfgVertex*>()) return;
// Actually add to work list.
src.setUser<DfgVertex*>(workListp);
workListp = &src;
});
// Remove the unused vertex
vtxp->unlinkDelete(dfg);
} }
// Finally remove unused constants // Finally remove unused constants