mirror of
https://github.com/verilator/verilator.git
synced 2025-04-21 12:06:55 +00:00
Dfg: Use a worklist driven algorithm for unused vertex removal
This improves verilation speed slightly.
This commit is contained in:
parent
fb9ec03c3f
commit
454efbe3fc
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user