2026-02-01 06:09:40 +01:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Reorder statements within always blocks
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// This program is free software; you can redistribute it and/or modify it
|
|
|
|
|
// under the terms of either the GNU Lesser General Public License Version 3
|
|
|
|
|
// or the Perl Artistic License Version 2.0.
|
|
|
|
|
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Reorder transformations:
|
|
|
|
|
//
|
2026-02-08 17:09:53 +01:00
|
|
|
// reorderAll() reorders statements within individual blocks to avoid
|
|
|
|
|
// shwdow variables use by non blocking assignments when possible.
|
|
|
|
|
// For exmaple, the left side needs a shadow variable for 'b', the
|
|
|
|
|
// right side does not:
|
|
|
|
|
// Bad: Good:
|
|
|
|
|
// b <= a; c <= b;
|
|
|
|
|
// c <= b; b <= a;
|
2026-02-01 06:09:40 +01:00
|
|
|
//
|
|
|
|
|
// The scoreboard tracks data deps as follows:
|
|
|
|
|
//
|
|
|
|
|
// ALWAYS
|
|
|
|
|
// ASSIGN ({var} <= {cons})
|
|
|
|
|
// Record as generating var_DLY (independent of use of var), consumers
|
|
|
|
|
// ASSIGN ({var} = {cons}
|
|
|
|
|
// Record generator and consumer
|
|
|
|
|
// Any var that is only consumed can be ignored.
|
|
|
|
|
// Then we split into separate ALWAYS blocks.
|
|
|
|
|
//
|
|
|
|
|
// The scoreboard includes innards of if/else nodes also. Splitting is no
|
|
|
|
|
// longer limited to top-level statements, we can split within if-else
|
|
|
|
|
// blocks. We want to be able to split this:
|
|
|
|
|
//
|
|
|
|
|
// 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}
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
|
|
|
|
#include "V3Reorder.h"
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
#include "V3EmitV.h"
|
2026-02-01 06:09:40 +01:00
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
#include <string>
|
2026-02-01 06:09:40 +01:00
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Support classes
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderNodeVertex VL_NOT_FINAL : public V3GraphVertex {
|
|
|
|
|
VL_RTTI_IMPL(ReorderNodeVertex, V3GraphVertex)
|
2026-02-01 06:09:40 +01:00
|
|
|
AstNode* const m_nodep;
|
|
|
|
|
|
|
|
|
|
protected:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderNodeVertex(V3Graph* graphp, AstNode* nodep)
|
2026-02-01 06:09:40 +01:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2026-02-08 17:09:53 +01:00
|
|
|
~ReorderNodeVertex() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
// ACCESSORS
|
|
|
|
|
// Do not make accessor for nodep(), It may change due to
|
|
|
|
|
// reordering a lower block, but we don't repair it
|
2026-02-08 17:09:53 +01:00
|
|
|
std::string name() const override {
|
|
|
|
|
std::string str = cvtToHex(m_nodep) + '\n';
|
|
|
|
|
if (AstVarScope* const vscp = VN_CAST(m_nodep, VarScope)) {
|
|
|
|
|
str += vscp->prettyName();
|
|
|
|
|
} else {
|
|
|
|
|
str += V3EmitV::debugVerilogForTree(m_nodep);
|
|
|
|
|
str = VString::quoteBackslash(str);
|
|
|
|
|
str = VString::quoteAny(str, '"', '\\');
|
|
|
|
|
str = VString::replaceSubstr(str, "\n", "\\l");
|
|
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
}
|
2026-02-01 06:09:40 +01:00
|
|
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
2026-02-08 17:09:53 +01:00
|
|
|
std::string dotShape() const override { return VN_IS(m_nodep, VarScope) ? "ellipse" : "box"; }
|
2026-02-01 06:09:40 +01:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
virtual AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderImpureVertex final : public ReorderNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(ReorderImpureVertex, ReorderNodeVertex)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
explicit ReorderImpureVertex(V3Graph* graphp, AstNode* nodep)
|
|
|
|
|
: ReorderNodeVertex{graphp, nodep} {}
|
|
|
|
|
~ReorderImpureVertex() override = default;
|
|
|
|
|
string name() const override VL_MT_STABLE { return "*IMPURE*"; }
|
|
|
|
|
string dotColor() const override { return "red"; }
|
2026-02-01 06:09:40 +01:00
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderLogicVertex final : public ReorderNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(ReorderLogicVertex, ReorderNodeVertex)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderLogicVertex(V3Graph* graphp, AstNode* nodep)
|
|
|
|
|
: ReorderNodeVertex{graphp, nodep} {}
|
|
|
|
|
~ReorderLogicVertex() override = default;
|
|
|
|
|
string dotColor() const override { return "black"; }
|
2026-02-01 06:09:40 +01:00
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderVarStdVertex final : public ReorderNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(ReorderVarStdVertex, ReorderNodeVertex)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderVarStdVertex(V3Graph* graphp, AstVarScope* nodep)
|
|
|
|
|
: ReorderNodeVertex{graphp, nodep} {}
|
|
|
|
|
~ReorderVarStdVertex() override = default;
|
|
|
|
|
string dotColor() const override { return "blue"; }
|
2026-02-01 06:09:40 +01:00
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderVarPostVertex final : public ReorderNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(ReorderVarPostVertex, ReorderNodeVertex)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderVarPostVertex(V3Graph* graphp, AstVarScope* nodep)
|
|
|
|
|
: ReorderNodeVertex{graphp, nodep} {}
|
|
|
|
|
~ReorderVarPostVertex() override = default;
|
|
|
|
|
string name() const override { return "POST "s + ReorderNodeVertex::name(); }
|
|
|
|
|
string dotColor() const override { return "green"; }
|
2026-02-01 06:09:40 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Edge types
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderEdge VL_NOT_FINAL : public V3GraphEdge {
|
|
|
|
|
VL_RTTI_IMPL(ReorderEdge, V3GraphEdge)
|
2026-02-01 06:09:40 +01:00
|
|
|
uint32_t m_ignoreInStep = 0; // Step number that if set to, causes this edge to be ignored
|
|
|
|
|
static uint32_t s_stepNum; // Global step number
|
|
|
|
|
protected:
|
|
|
|
|
static constexpr int WEIGHT_NORMAL = 10;
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, bool cutable)
|
|
|
|
|
: V3GraphEdge{graphp, fromp, top, WEIGHT_NORMAL, cutable} {}
|
|
|
|
|
~ReorderEdge() override = default;
|
|
|
|
|
|
|
|
|
|
virtual bool followScoreboard() const = 0;
|
|
|
|
|
|
|
|
|
|
std::string dotStyle() const override {
|
|
|
|
|
return ignoreThisStep() ? "dotted" : V3GraphEdge::dotStyle();
|
|
|
|
|
}
|
2026-02-01 06:09:40 +01:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Iterator for graph functions
|
|
|
|
|
static void incrementStep() { ++s_stepNum; }
|
|
|
|
|
bool ignoreThisStep() const { return m_ignoreInStep == s_stepNum; }
|
|
|
|
|
void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; }
|
2026-02-08 17:09:53 +01:00
|
|
|
|
2026-02-01 06:09:40 +01:00
|
|
|
static bool followScoreboard(const V3GraphEdge* edgep) {
|
2026-02-08 17:09:53 +01:00
|
|
|
const ReorderEdge& edge = *edgep->as<ReorderEdge>();
|
|
|
|
|
return !edge.ignoreThisStep() && edge.followScoreboard();
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
static bool followCyclic(const V3GraphEdge* edgep) {
|
2026-02-08 17:09:53 +01:00
|
|
|
const ReorderEdge& edge = *edgep->as<ReorderEdge>();
|
|
|
|
|
return !edge.ignoreThisStep();
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
};
|
2026-02-08 17:09:53 +01:00
|
|
|
uint32_t ReorderEdge::s_stepNum = 0;
|
2026-02-01 06:09:40 +01:00
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderPostEdge final : public ReorderEdge {
|
|
|
|
|
VL_RTTI_IMPL(ReorderPostEdge, ReorderEdge)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderPostEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
|
|
|
|
: ReorderEdge{graphp, fromp, top, CUTABLE} {}
|
|
|
|
|
~ReorderPostEdge() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
bool followScoreboard() const override { return false; }
|
|
|
|
|
string dotColor() const override { return "khaki"; }
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderLVEdge final : public ReorderEdge {
|
|
|
|
|
VL_RTTI_IMPL(ReorderLVEdge, ReorderEdge)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderLVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
|
|
|
|
: ReorderEdge{graphp, fromp, top, CUTABLE} {}
|
|
|
|
|
~ReorderLVEdge() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
bool followScoreboard() const override { return true; }
|
|
|
|
|
string dotColor() const override { return "yellowGreen"; }
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderRVEdge final : public ReorderEdge {
|
|
|
|
|
VL_RTTI_IMPL(ReorderRVEdge, ReorderEdge)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderRVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
|
|
|
|
: ReorderEdge{graphp, fromp, top, CUTABLE} {}
|
|
|
|
|
~ReorderRVEdge() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
bool followScoreboard() const override { return true; }
|
|
|
|
|
string dotColor() const override { return "green"; }
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderScorebdEdge final : public ReorderEdge {
|
|
|
|
|
VL_RTTI_IMPL(ReorderScorebdEdge, ReorderEdge)
|
2026-02-01 06:09:40 +01:00
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderScorebdEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
|
|
|
|
: ReorderEdge{graphp, fromp, top, CUTABLE} {}
|
|
|
|
|
~ReorderScorebdEdge() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
bool followScoreboard() const override { return true; }
|
|
|
|
|
string dotColor() const override { return "blue"; }
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderStrictEdge final : public ReorderEdge {
|
|
|
|
|
VL_RTTI_IMPL(ReorderStrictEdge, ReorderEdge)
|
2026-02-01 06:09:40 +01:00
|
|
|
// A strict order, based on the original statement order in the graph
|
|
|
|
|
// The only non-cutable edge type
|
|
|
|
|
public:
|
2026-02-08 17:09:53 +01:00
|
|
|
ReorderStrictEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
|
|
|
|
: ReorderEdge{graphp, fromp, top, NOT_CUTABLE} {}
|
|
|
|
|
~ReorderStrictEdge() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
bool followScoreboard() const override { return true; }
|
|
|
|
|
string dotColor() const override { return "blue"; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2026-02-08 17:09:53 +01:00
|
|
|
// Reorder class functions
|
2026-02-01 06:09:40 +01:00
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
class ReorderVisitor final : public VNVisitor {
|
|
|
|
|
// NODE STATE - Only under AstAlways
|
|
|
|
|
// AstVarScope::user1p -> Var ReorderVarStdVertex* for usage var, 0=not set yet
|
|
|
|
|
// AstVarScope::user2p -> Var ReorderVarPostVertex* for delayed assignment var, 0=not set yet
|
|
|
|
|
// Ast*::user3p -> Statement ReorderLogicVertex* (temporary only)
|
|
|
|
|
// Ast*::user4 -> Current ordering number (reorderBlock usage)
|
2026-02-01 06:09:40 +01:00
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
// STATE
|
|
|
|
|
V3Graph* m_graphp = nullptr; // Scoreboard of var usages/dependencies
|
|
|
|
|
ReorderImpureVertex* m_impureVtxp = nullptr; // Element specifying PLI ordering
|
|
|
|
|
bool m_inDly = false; // Inside ASSIGNDLY
|
|
|
|
|
const char* m_noReorderWhy = nullptr; // Reason we can't reorder
|
|
|
|
|
std::vector<ReorderLogicVertex*> m_stmtStackps; // Current statements being tracked
|
2026-02-01 06:09:40 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void cleanupBlockGraph(AstNode* nodep) {
|
|
|
|
|
// Transform the graph into what we need
|
|
|
|
|
UINFO(5, "ReorderBlock " << nodep);
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
// Simplify graph by removing redundant edges
|
|
|
|
|
m_graphp->removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("reorderg_nodup", false);
|
2026-02-01 06:09:40 +01:00
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
// Mark all the logic for this step by setting Vertex::user() to true
|
|
|
|
|
m_graphp->userClearVertices();
|
2026-02-01 06:09:40 +01:00
|
|
|
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
|
2026-02-08 17:09:53 +01:00
|
|
|
nextp->user3u().to<ReorderLogicVertex*>()->user(true);
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
// New step
|
|
|
|
|
ReorderEdge::incrementStep();
|
|
|
|
|
|
2026-02-01 06:09:40 +01:00
|
|
|
// If a var vertex has only inputs, it's a input-only node,
|
|
|
|
|
// and can be ignored for coloring **this block only**
|
2026-02-08 17:09:53 +01:00
|
|
|
for (V3GraphVertex& vtx : m_graphp->vertices()) {
|
|
|
|
|
if (!vtx.outEmpty()) continue;
|
|
|
|
|
if (!vtx.is<ReorderVarStdVertex>()) continue;
|
|
|
|
|
for (V3GraphEdge& edge : vtx.inEdges()) edge.as<ReorderEdge>()->setIgnoreThisStep();
|
|
|
|
|
}
|
2026-02-01 06:09:40 +01:00
|
|
|
|
|
|
|
|
// For reordering this single block only, mark all logic
|
|
|
|
|
// vertexes not involved with this step as unimportant
|
2026-02-08 17:09:53 +01:00
|
|
|
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
|
|
|
|
if (vertex.user()) continue;
|
|
|
|
|
if (!vertex.is<ReorderLogicVertex>()) continue;
|
|
|
|
|
for (V3GraphEdge& edge : vertex.inEdges()) edge.as<ReorderEdge>()->setIgnoreThisStep();
|
|
|
|
|
for (V3GraphEdge& edge : vertex.outEdges())
|
|
|
|
|
edge.as<ReorderEdge>()->setIgnoreThisStep();
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Weak coloring to determine what needs to remain in order
|
|
|
|
|
// This follows all step-relevant edges excluding PostEdges, which are done later
|
2026-02-08 17:09:53 +01:00
|
|
|
m_graphp->weaklyConnected(&ReorderEdge::followScoreboard);
|
2026-02-01 06:09:40 +01:00
|
|
|
|
|
|
|
|
// Add hard orderings between all nodes of same color, in the order they appeared
|
2026-02-08 17:09:53 +01:00
|
|
|
std::unordered_map<uint32_t, ReorderLogicVertex*> lastOfColor;
|
|
|
|
|
for (AstNode* currp = nodep; currp; currp = currp->nextp()) {
|
|
|
|
|
ReorderLogicVertex* const vtxp = currp->user3u().to<ReorderLogicVertex*>();
|
|
|
|
|
const uint32_t color = vtxp->color();
|
|
|
|
|
UASSERT_OBJ(color, currp, "No node color assigned");
|
|
|
|
|
if (lastOfColor[color]) new ReorderStrictEdge{m_graphp, lastOfColor[color], vtxp};
|
|
|
|
|
lastOfColor[color] = vtxp;
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2026-02-08 17:09:53 +01:00
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("reorderg_pre", false);
|
|
|
|
|
m_graphp->acyclic(&ReorderEdge::followCyclic);
|
|
|
|
|
m_graphp->rank(&ReorderEdge::followCyclic); // Or order(), but that's more expensive
|
|
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("reorderg_opt", false);
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2026-02-08 17:09:53 +01:00
|
|
|
for (AstNode* currp = nodep; currp; currp = currp->nextp()) {
|
|
|
|
|
const ReorderLogicVertex* const vtxp = currp->user3u().to<ReorderLogicVertex*>();
|
|
|
|
|
rankMap.emplace(vtxp->rank(), currp);
|
|
|
|
|
currp->user4(++currOrder); // Record current ordering
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Is the current ordering OK?
|
|
|
|
|
bool leaveAlone = true;
|
|
|
|
|
int newOrder = 0; // New sequence number of assignment
|
2026-02-08 17:09:53 +01:00
|
|
|
for (const auto& item : rankMap) {
|
|
|
|
|
const AstNode* const nextp = item.second;
|
2026-02-01 06:09:40 +01:00
|
|
|
if (++newOrder != nextp->user4()) leaveAlone = false;
|
|
|
|
|
}
|
|
|
|
|
if (leaveAlone) {
|
|
|
|
|
UINFO(6, " No changes");
|
2026-02-08 17:09:53 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VNRelinker replaceHandle; // Where to add the list
|
|
|
|
|
AstNode* newListp = nullptr;
|
|
|
|
|
for (const auto& item : rankMap) {
|
|
|
|
|
AstNode* const nextp = item.second;
|
|
|
|
|
UINFO(6, " New order: " << nextp);
|
|
|
|
|
nextp->unlinkFrBack(nextp == nodep ? &replaceHandle : nullptr);
|
|
|
|
|
newListp = AstNode::addNext(newListp, nextp);
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
2026-02-08 17:09:53 +01:00
|
|
|
replaceHandle.relink(newListp);
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void processBlock(AstNode* nodep) {
|
2026-02-08 17:09:53 +01:00
|
|
|
if (m_noReorderWhy) return;
|
|
|
|
|
|
|
|
|
|
// Empty lists are ignorable
|
|
|
|
|
if (!nodep) return;
|
|
|
|
|
UASSERT_OBJ(nodep->firstAbovep(), nodep, "Node passed is in not head of list");
|
|
|
|
|
UASSERT_OBJ(!nodep->user3p(), nodep, "Should not have a logic vertex");
|
|
|
|
|
|
|
|
|
|
// It nothing to reorder with, just iterate
|
2026-02-01 06:09:40 +01:00
|
|
|
if (!nodep->nextp()) {
|
|
|
|
|
iterate(nodep);
|
2026-02-08 17:09:53 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process it
|
|
|
|
|
UINFO(9, " processBlock " << nodep);
|
|
|
|
|
|
|
|
|
|
// Iterate across current block, making the scoreboard
|
|
|
|
|
for (AstNode* currp = nodep; currp; currp = currp->nextp()) {
|
|
|
|
|
// Create the logic vertex for this statement
|
|
|
|
|
UASSERT_OBJ(!currp->user3p(), currp, "user3p should not be set");
|
|
|
|
|
ReorderLogicVertex* const vtxp = new ReorderLogicVertex{m_graphp, currp};
|
|
|
|
|
currp->user3p(vtxp);
|
|
|
|
|
|
|
|
|
|
// Visit the statement - this can recursively reorder sub statements
|
|
|
|
|
m_stmtStackps.push_back(vtxp);
|
|
|
|
|
iterate(currp);
|
|
|
|
|
m_stmtStackps.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_noReorderWhy) { // Jump or something nasty
|
|
|
|
|
UINFO(9, " NoReorderBlock because " << m_noReorderWhy);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reorder statements in this block
|
|
|
|
|
cleanupBlockGraph(nodep);
|
|
|
|
|
reorderBlock(nodep);
|
|
|
|
|
|
|
|
|
|
// 'nodep' might no longer be the head of the list, rewind
|
|
|
|
|
while (nodep->backp()->nextp() == nodep) nodep = nodep->backp();
|
|
|
|
|
|
|
|
|
|
// Delete vertexes and edges only applying to this block
|
|
|
|
|
for (AstNode* currp = nodep; currp; currp = currp->nextp()) {
|
|
|
|
|
currp->user3u().to<ReorderLogicVertex*>()->unlinkDelete(m_graphp);
|
|
|
|
|
currp->user3p(nullptr);
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
// VISITORS
|
2026-02-01 06:09:40 +01:00
|
|
|
void visit(AstAlways* nodep) override {
|
2026-02-08 17:09:53 +01:00
|
|
|
UASSERT_OBJ(!m_graphp, nodep, "AstAlways should not nest");
|
|
|
|
|
VL_RESTORER(m_graphp);
|
|
|
|
|
VL_RESTORER(m_impureVtxp);
|
|
|
|
|
VL_RESTORER(m_inDly);
|
|
|
|
|
VL_RESTORER(m_noReorderWhy);
|
|
|
|
|
|
|
|
|
|
V3Graph graph;
|
|
|
|
|
m_graphp = &graph;
|
|
|
|
|
m_impureVtxp = nullptr;
|
|
|
|
|
m_inDly = false;
|
|
|
|
|
m_noReorderWhy = nullptr;
|
|
|
|
|
|
|
|
|
|
const VNUser1InUse user1InUse;
|
|
|
|
|
const VNUser2InUse user2InUse;
|
|
|
|
|
const VNUser3InUse user3InUse;
|
|
|
|
|
const VNUser4InUse user4InUse;
|
|
|
|
|
|
2026-02-01 06:09:40 +01:00
|
|
|
UINFOTREE(9, nodep, "", "alwIn:");
|
|
|
|
|
processBlock(nodep->stmtsp());
|
|
|
|
|
UINFOTREE(9, nodep, "", "alwOut");
|
2026-02-08 17:09:53 +01:00
|
|
|
|
|
|
|
|
m_stmtStackps.clear();
|
2026-02-01 06:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstNodeIf* nodep) override {
|
2026-02-08 17:09:53 +01:00
|
|
|
if (!m_graphp || m_noReorderWhy) return;
|
2026-02-01 06:09:40 +01:00
|
|
|
iterateAndNextNull(nodep->condp());
|
|
|
|
|
processBlock(nodep->thensp());
|
|
|
|
|
processBlock(nodep->elsesp());
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 17:09:53 +01:00
|
|
|
void visit(AstJumpGo* nodep) override {
|
|
|
|
|
if (!m_graphp || m_noReorderWhy) return;
|
|
|
|
|
m_noReorderWhy = "JumpGo";
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstExprStmt* nodep) override {
|
|
|
|
|
if (!m_graphp || m_noReorderWhy) return;
|
|
|
|
|
VL_RESTORER(m_inDly);
|
|
|
|
|
m_inDly = false;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstAssignDly* nodep) override {
|
|
|
|
|
if (!m_graphp || m_noReorderWhy) return;
|
|
|
|
|
iterate(nodep->rhsp());
|
|
|
|
|
VL_RESTORER(m_inDly);
|
|
|
|
|
m_inDly = true;
|
|
|
|
|
iterate(nodep->lhsp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstVarRef* nodep) override {
|
|
|
|
|
if (!m_graphp || m_noReorderWhy) return;
|
|
|
|
|
if (m_stmtStackps.empty()) return;
|
|
|
|
|
|
|
|
|
|
// Reads of constants can be ignored - TODO: This should be "constexpr", not run-time const
|
|
|
|
|
if (nodep->varp()->isConst()) return;
|
|
|
|
|
|
|
|
|
|
// SPEEDUP: We add duplicate edges, that should be fixed
|
|
|
|
|
|
|
|
|
|
AstVarScope* const vscp = nodep->varScopep();
|
|
|
|
|
|
|
|
|
|
// Create vertexes for variable
|
|
|
|
|
if (!vscp->user1p()) vscp->user1p(new ReorderVarStdVertex{m_graphp, vscp});
|
|
|
|
|
ReorderVarStdVertex* const vstdp = vscp->user1u().to<ReorderVarStdVertex*>();
|
|
|
|
|
|
|
|
|
|
// Variable is read
|
|
|
|
|
if (nodep->access().isReadOnly()) {
|
|
|
|
|
for (ReorderLogicVertex* const vtxp : m_stmtStackps) {
|
|
|
|
|
new ReorderRVEdge{m_graphp, vtxp, vstdp};
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Variable is written, not NBA
|
|
|
|
|
if (!m_inDly) {
|
|
|
|
|
for (ReorderLogicVertex* const vtxp : m_stmtStackps) {
|
|
|
|
|
new ReorderLVEdge{m_graphp, vstdp, vtxp};
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Variable is written by NBA
|
|
|
|
|
if (!vscp->user2p()) {
|
|
|
|
|
ReorderVarPostVertex* const vpostp = new ReorderVarPostVertex{m_graphp, vscp};
|
|
|
|
|
vscp->user2p(vpostp);
|
|
|
|
|
new ReorderPostEdge{m_graphp, vstdp, vpostp};
|
|
|
|
|
}
|
|
|
|
|
ReorderVarPostVertex* const vpostp = vscp->user2u().to<ReorderVarPostVertex*>();
|
|
|
|
|
for (ReorderLogicVertex* const vtxp : m_stmtStackps) {
|
|
|
|
|
new ReorderLVEdge{m_graphp, vpostp, vtxp};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstNode* nodep) override {
|
|
|
|
|
// Outside AstAlways, just descend
|
|
|
|
|
if (!m_graphp) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Early exit if decided not to reorder
|
|
|
|
|
if (m_noReorderWhy) return;
|
|
|
|
|
|
|
|
|
|
// Timing control prevents reordering
|
|
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
m_noReorderWhy = "TimingControl";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Order all impure statements with other impure statements
|
|
|
|
|
if (!nodep->isPure()) {
|
|
|
|
|
if (!m_impureVtxp) m_impureVtxp = new ReorderImpureVertex{m_graphp, nodep};
|
|
|
|
|
// This edge is only used to find weakly connected components, so one edge is enough
|
|
|
|
|
for (ReorderLogicVertex* const vtxp : m_stmtStackps) {
|
|
|
|
|
new ReorderScorebdEdge{m_graphp, m_impureVtxp, vtxp};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
public:
|
|
|
|
|
explicit ReorderVisitor(AstNetlist* nodep) { iterate(nodep); }
|
|
|
|
|
~ReorderVisitor() override = default;
|
2026-02-01 06:09:40 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// V3Reorder class functions
|
|
|
|
|
|
|
|
|
|
void V3Reorder::reorderAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
|
|
|
{ ReorderVisitor{nodep}; } // Destruct before checking
|
|
|
|
|
V3Global::dumpCheckGlobalTree("reorder", 0, dumpTreeEitherLevel() >= 3);
|
|
|
|
|
}
|