Internals: Split reorder transfrom from V3Split to V3Reorder (#6976)

This commit is contained in:
Geza Lore
2026-02-01 05:09:40 +00:00
committed by GitHub
parent f0afcede10
commit 197f11044e
7 changed files with 654 additions and 213 deletions
+8 -209
View File
@@ -13,17 +13,12 @@
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Split implements two separate transformations:
// splitAlwaysAll() splits large always blocks into smaller always blocks
// V3Split transformation:
//
// splitAll() splits large always blocks into smaller always blocks
// when possible (but does not change the order of statements relative
// to one another.)
//
// splitReorderAll() reorders statements within individual blocks
// to avoid delay vars when possible. It no longer splits always blocks.
//
// Both use a common base class, and common graph-building code to reflect
// data dependencies within an always block (the "scoreboard".)
//
// The scoreboard tracks data deps as follows:
//
// ALWAYS
@@ -56,25 +51,6 @@
// better. Later modules (V3Gate, V3Order) run faster if they aren't
// handling enormous blocks with long lists of inputs and outputs.
//
// Furthermore, the optional reorder routine can optimize this:
// NODEASSIGN/NODEIF/WHILE
// S1: ASSIGN {v1} <= 0. // Duplicate of below
// S2: ASSIGN {v1} <= {v0}
// S3: IF (...,
// X1: ASSIGN {v2} <= {v1}
// X2: ASSIGN {v3} <= {v2}
// We'd like to swap S2 and S3, and X1 and X2.
//
// Create a graph in split assignment order.
// v3 -breakable-> v3Dly --> X2 --> v2 -brk-> v2Dly -> X1 -> v1
// Likewise on each "upper" statement vertex
// v3Dly & v2Dly -> S3 -> v1 & v2
// v1 -brk-> v1Dly -> S2 -> v0
// v1Dly -> S1 -> {empty}
// Multiple assignments to the same variable must remain in order
//
// Also vars must not be "public" and we also scoreboard nodep->isPure()
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
@@ -90,6 +66,8 @@
VL_DEFINE_DEBUG_FUNCTIONS;
namespace {
//######################################################################
// Support classes
@@ -444,182 +422,6 @@ private:
VL_UNCOPYABLE(SplitReorderBaseVisitor);
};
class ReorderVisitor final : public SplitReorderBaseVisitor {
// CONSTRUCTORS
public:
explicit ReorderVisitor(AstNetlist* nodep) { iterate(nodep); }
~ReorderVisitor() override = default;
// METHODS
protected:
void makeRvalueEdges(SplitVarStdVertex* vstdp) override {
for (SplitLogicVertex* vxp : m_stmtStackps) new SplitRVEdge{&m_graph, vxp, vstdp};
}
void cleanupBlockGraph(AstNode* nodep) {
// Transform the graph into what we need
UINFO(5, "ReorderBlock " << nodep);
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("reorderg_nodup", false);
// Mark all the logic for this step
// Vertex::m_user begin: true indicates logic for this step
m_graph.userClearVertices();
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
SplitLogicVertex* const vvertexp
= reinterpret_cast<SplitLogicVertex*>(nextp->user3p());
vvertexp->user(true);
}
// If a var vertex has only inputs, it's a input-only node,
// and can be ignored for coloring **this block only**
SplitEdge::incrementStep();
pruneDepsOnInputs();
// For reordering this single block only, mark all logic
// vertexes not involved with this step as unimportant
for (V3GraphVertex& vertex : m_graph.vertices()) {
if (!vertex.user()) {
if (vertex.is<SplitLogicVertex>()) {
for (V3GraphEdge& edge : vertex.inEdges()) {
SplitEdge& oedge = static_cast<SplitEdge&>(edge);
oedge.setIgnoreThisStep();
}
for (V3GraphEdge& edge : vertex.outEdges()) {
SplitEdge& oedge = static_cast<SplitEdge&>(edge);
oedge.setIgnoreThisStep();
}
}
}
}
// Weak coloring to determine what needs to remain in order
// This follows all step-relevant edges excluding PostEdges, which are done later
m_graph.weaklyConnected(&SplitEdge::followScoreboard);
// Add hard orderings between all nodes of same color, in the order they appeared
std::unordered_map<uint32_t, SplitLogicVertex*> lastOfColor;
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
SplitLogicVertex* const vvertexp
= reinterpret_cast<SplitLogicVertex*>(nextp->user3p());
const uint32_t color = vvertexp->color();
UASSERT_OBJ(color, nextp, "No node color assigned");
if (lastOfColor[color]) {
new SplitStrictEdge{&m_graph, lastOfColor[color], vvertexp};
}
lastOfColor[color] = vvertexp;
}
// And a real ordering to get the statements into something reasonable
// We don't care if there's cutable violations here...
// Non-cutable violations should be impossible; as those edges are program-order
if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("splitg_preo", false);
m_graph.acyclic(&SplitEdge::followCyclic);
m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive
if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("splitg_opt", false);
}
void reorderBlock(AstNode* nodep) {
// Reorder statements in the completed graph
// Map the rank numbers into nodes they associate with
std::multimap<uint32_t, AstNode*> rankMap;
int currOrder = 0; // Existing sequence number of assignment
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
const SplitLogicVertex* const vvertexp
= reinterpret_cast<SplitLogicVertex*>(nextp->user3p());
rankMap.emplace(vvertexp->rank(), nextp);
nextp->user4(++currOrder); // Record current ordering
}
// Is the current ordering OK?
bool leaveAlone = true;
int newOrder = 0; // New sequence number of assignment
for (auto it = rankMap.cbegin(); it != rankMap.cend(); ++it) {
const AstNode* const nextp = it->second;
if (++newOrder != nextp->user4()) leaveAlone = false;
}
if (leaveAlone) {
UINFO(6, " No changes");
} else {
VNRelinker replaceHandle; // Where to add the list
AstNode* newListp = nullptr;
for (auto it = rankMap.cbegin(); it != rankMap.cend(); ++it) {
AstNode* const nextp = it->second;
UINFO(6, " New order: " << nextp);
if (nextp == nodep) {
nodep->unlinkFrBack(&replaceHandle);
} else {
nextp->unlinkFrBack();
}
if (newListp) {
newListp = newListp->addNext(nextp);
} else {
newListp = nextp;
}
}
replaceHandle.relink(newListp);
}
}
void processBlock(AstNode* nodep) {
if (!nodep) return; // Empty lists are ignorable
// Pass the first node in a list of block items, we'll process them
// Check there's >= 2 sub statements, else nothing to analyze
// Save recursion state
AstNode* firstp = nodep; // We may reorder, and nodep is no longer first.
void* const oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it
nodep->user3p(nullptr);
UASSERT_OBJ(nodep->firstAbovep(), nodep,
"Node passed is in next list; should have processed all list at once");
// Process it
if (!nodep->nextp()) {
// Just one, so can't reorder. Just look for more blocks/statements.
iterate(nodep);
} else {
UINFO(9, " processBlock " << nodep);
// Process block and followers
scanBlock(nodep);
if (m_noReorderWhy != "") { // Jump or something nasty
UINFO(9, " NoReorderBlock because " << m_noReorderWhy);
} else {
// Reorder statements in this block
cleanupBlockGraph(nodep);
reorderBlock(nodep);
// Delete old vertexes and edges only applying to this block
// First, walk back to first in list
while (firstp->backp()->nextp() == firstp) firstp = firstp->backp();
for (AstNode* nextp = firstp; nextp; nextp = nextp->nextp()) {
SplitLogicVertex* const vvertexp
= reinterpret_cast<SplitLogicVertex*>(nextp->user3p());
vvertexp->unlinkDelete(&m_graph);
}
}
}
// Again, nodep may no longer be first.
firstp->user3p(oldBlockUser3);
}
void visit(AstAlways* nodep) override {
UINFO(4, " ALW " << nodep);
UINFOTREE(9, nodep, "", "alwIn:");
scoreboardClear();
processBlock(nodep->stmtsp());
UINFOTREE(9, nodep, "", "alwOut");
}
void visit(AstNodeIf* nodep) override {
UINFO(4, " IF " << nodep);
iterateAndNextNull(nodep->condp());
processBlock(nodep->thensp());
processBlock(nodep->elsesp());
}
private:
VL_UNCOPYABLE(ReorderVisitor);
};
using ColorSet = std::unordered_set<uint32_t>;
using AlwaysVec = std::vector<AstAlways*>;
@@ -1002,15 +804,12 @@ private:
VL_UNCOPYABLE(SplitVisitor);
};
} //namespace
//######################################################################
// Split class functions
void V3Split::splitReorderAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ ReorderVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("reorder", 0, dumpTreeEitherLevel() >= 3);
}
void V3Split::splitAlwaysAll(AstNetlist* nodep) {
void V3Split::splitAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ SplitVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("split", 0, dumpTreeEitherLevel() >= 3);