2012-04-12 21:08:20 -04:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
//*************************************************************************
|
2026-09-17 23:11:26 +02:00
|
|
|
// DESCRIPTION: Verilator: Break always into separate statements
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
2019-11-07 22:33:59 -05:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2026-01-26 20:24:34 -05:00
|
|
|
// 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
|
2020-03-21 11:24:24 -04:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2026-02-01 05:09:40 +00:00
|
|
|
// V3Split transformation:
|
|
|
|
|
//
|
2026-09-17 23:11:26 +02:00
|
|
|
// splitAll() splits large always blocks into smaller always blocks when
|
|
|
|
|
// possible, without changing the order of dependent statements relative to
|
|
|
|
|
// one another. Splitting is not limited to top-level statements, if-else
|
|
|
|
|
// blocks can also be split, so that:
|
2018-02-28 06:58:41 -05:00
|
|
|
//
|
|
|
|
|
// always @ (...) begin
|
|
|
|
|
// if (reset) begin
|
|
|
|
|
// a <= 0;
|
|
|
|
|
// b <= 0;
|
|
|
|
|
// // ... ten thousand more
|
|
|
|
|
// end
|
|
|
|
|
// else begin
|
|
|
|
|
// a <= a_in;
|
|
|
|
|
// b <= b_in;
|
|
|
|
|
// // ... ten thousand more
|
|
|
|
|
// end
|
|
|
|
|
// end
|
|
|
|
|
//
|
2026-09-17 23:11:26 +02:00
|
|
|
// becomes a separate block for each of a, b, and so on. Even though this
|
|
|
|
|
// requires duplicating the conditional many times, it's usually better as it
|
|
|
|
|
// reduces ordering constraints, and later optimizations can merge
|
|
|
|
|
// conditionals.
|
|
|
|
|
//
|
|
|
|
|
// To find what must stay together, a graph is built per always block, holding
|
|
|
|
|
// a vertex per 'leaf' and 'if' statement, and up to two vertices per variable.
|
|
|
|
|
// Statements in the same connected component must stay in one block, and each
|
|
|
|
|
// component then becomes a block of its own. The edges are:
|
|
|
|
|
//
|
|
|
|
|
// - Blocking write: variable -> statement. Such a write is observable within
|
|
|
|
|
// the block, so the readers of the variable stay with the writer.
|
|
|
|
|
// - Non-blocking write: a separate 'post' vertex of the variable -> statement.
|
|
|
|
|
// All writers of a variable stay together, but the readers, which see the
|
|
|
|
|
// value from before the NBA commits, are not held together with them.
|
|
|
|
|
// - Read: statement -> variable. For an 'if', only the reads in its own
|
|
|
|
|
// condition count, not those in its branches.
|
|
|
|
|
// - Impure statement: statement -> a vertex shared by all of them, so that
|
|
|
|
|
// $display, DPI calls, etc stay in one block, in order.
|
|
|
|
|
//
|
|
|
|
|
// A variable with no blocking write is an input to the block, so its vertex is
|
|
|
|
|
// removed, and with it the dependencies on it, as two statements both reading
|
|
|
|
|
// an input need not stay together. An 'if' left with no dependencies of its
|
|
|
|
|
// own is removed likewise, so that the statements under it can separate, each
|
|
|
|
|
// taking a copy of the condition.
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-04 20:17:11 -04:00
|
|
|
|
2023-10-18 06:37:46 -04:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#include "V3Split.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#include "V3Graph.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
#include "V3Stats.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
#include <string>
|
2022-08-05 10:56:57 +01:00
|
|
|
#include <vector>
|
2018-10-14 13:43:24 -04:00
|
|
|
|
2022-09-18 20:53:42 +01:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
|
|
|
|
// Support classes
|
|
|
|
|
|
2020-11-18 21:32:16 -05:00
|
|
|
class SplitNodeVertex VL_NOT_FINAL : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(SplitNodeVertex, V3GraphVertex)
|
2021-11-13 13:50:44 -05:00
|
|
|
AstNode* const m_nodep;
|
2020-04-15 07:58:34 -04:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
protected:
|
|
|
|
|
SplitNodeVertex(V3Graph* graphp, AstNode* nodep)
|
2020-08-16 09:55:36 -04:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2017-11-27 20:11:34 -05:00
|
|
|
// ACCESSORS
|
2026-09-17 23:11:26 +02:00
|
|
|
std::string name() const override {
|
|
|
|
|
return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName();
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
public:
|
2026-09-17 23:11:26 +02:00
|
|
|
AstNode* nodep() const { return m_nodep; }
|
2006-08-26 11:35:28 +00:00
|
|
|
};
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
class SplitImpureVertex final : public SplitNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(SplitImpureVertex, SplitNodeVertex)
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
std::string name() const override { return "*IMPURE*"; }
|
|
|
|
|
std::string dotColor() const override { return "green"; }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2026-09-16 22:44:19 +02:00
|
|
|
public:
|
2026-09-17 23:11:26 +02:00
|
|
|
explicit SplitImpureVertex(V3Graph* graphp, AstNode* nodep)
|
2026-09-16 22:44:19 +02:00
|
|
|
: SplitNodeVertex{graphp, nodep} {}
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
class SplitStmtVertex final : public SplitNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(SplitStmtVertex, SplitNodeVertex)
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
std::string dotColor() const override { return "yellow"; }
|
2018-02-28 06:58:41 -05:00
|
|
|
|
|
|
|
|
public:
|
2026-09-17 23:11:26 +02:00
|
|
|
SplitStmtVertex(V3Graph* graphp, AstNode* nodep)
|
|
|
|
|
: SplitNodeVertex{graphp, nodep} {}
|
2018-02-28 06:58:41 -05:00
|
|
|
};
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
class SplitVarStdVertex final : public SplitNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(SplitVarStdVertex, SplitNodeVertex)
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
std::string dotColor() const override { return "skyblue"; }
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-16 22:44:19 +02:00
|
|
|
public:
|
2026-09-17 23:11:26 +02:00
|
|
|
SplitVarStdVertex(V3Graph* graphp, AstVarScope* vscp)
|
|
|
|
|
: SplitNodeVertex{graphp, vscp} {}
|
2026-09-16 22:44:19 +02:00
|
|
|
};
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
class SplitVarPostVertex final : public SplitNodeVertex {
|
|
|
|
|
VL_RTTI_IMPL(SplitVarPostVertex, SplitNodeVertex)
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
std::string name() const override { return "POST "s + SplitNodeVertex::name(); }
|
|
|
|
|
std::string dotColor() const override { return "CadetBlue"; }
|
2026-09-16 22:44:19 +02:00
|
|
|
|
|
|
|
|
public:
|
2026-09-17 23:11:26 +02:00
|
|
|
SplitVarPostVertex(V3Graph* graphp, AstVarScope* vscp)
|
|
|
|
|
: SplitNodeVertex{graphp, vscp} {}
|
2026-09-16 22:44:19 +02:00
|
|
|
};
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
class SplitVisitor final : public VNVisitor {
|
|
|
|
|
// NODE STATE - Only under AstAlways
|
|
|
|
|
// AstVarScope::user1p -> SplitVarStdVertex*: Regular program-flow variable vertex
|
|
|
|
|
// AstVarScope::user2p -> SplitVarPostVertex*: NBA written delayed variable vertex
|
|
|
|
|
// Ast{StmtIsh}::user1p -> SplitStmtVertex*
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// AstAlways::user3 -> bool: Block created by splitting, needs no further splitting
|
|
|
|
|
const VNUser3InUse m_inuser3;
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// STATE
|
|
|
|
|
V3Graph* m_graphp = nullptr; // Dependency graph to analyze statement connectivity
|
|
|
|
|
std::vector<SplitStmtVertex*> m_stmtStackps; // Current statements being tracked
|
|
|
|
|
SplitImpureVertex* m_impureVtxp = nullptr; // Vertex connecting impure statements
|
|
|
|
|
const char* m_noSplitWhy = nullptr; // Reason current block cannot be split
|
|
|
|
|
bool m_inDly = false; // Inside AstAssignDly Lhs
|
|
|
|
|
const AstIf* m_currIfp = nullptr; // The AstIf whose condition is currently visited
|
|
|
|
|
VDouble0 m_statSplits; // Statistic tracking
|
2026-09-16 22:44:19 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2026-09-17 23:11:26 +02:00
|
|
|
void addEdge(V3GraphVertex* fromp, V3GraphVertex* top) {
|
|
|
|
|
new V3GraphEdge{m_graphp, fromp, top, 1};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterate the given list of statements, building the dependency graph
|
|
|
|
|
void scanBlock(AstNode* stmtsp) {
|
|
|
|
|
if (m_noSplitWhy) return;
|
|
|
|
|
for (AstNode* stmtp = stmtsp; stmtp; stmtp = stmtp->nextp()) {
|
|
|
|
|
// Skip comments. They have no dependencies at all, so would always
|
|
|
|
|
// form an independent component, and hence a split block, of their
|
|
|
|
|
// own, which would be subsequently deleted as it does nothing.
|
|
|
|
|
if (VN_IS(stmtp, Comment)) continue;
|
|
|
|
|
UASSERT_OBJ(!stmtp->user1p(), stmtp, "user1p should not be set");
|
|
|
|
|
SplitStmtVertex* const vtxp = new SplitStmtVertex{m_graphp, stmtp};
|
|
|
|
|
stmtp->user1p(vtxp);
|
|
|
|
|
m_stmtStackps.push_back(vtxp);
|
|
|
|
|
iterate(stmtp);
|
|
|
|
|
m_stmtStackps.pop_back();
|
2026-09-16 22:25:36 +02:00
|
|
|
}
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Remove unnecessary edges, then color to find weakly connected components
|
|
|
|
|
uint32_t colorAlwaysGraph() {
|
|
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("splitg_built", false);
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Prune duplicate edges. Not necessary for correctness, but simplifies dumps.
|
|
|
|
|
m_graphp->removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("splitg_nodup", false);
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Remove variable vertices that are not written in the block.
|
|
|
|
|
// These are input-only to the block, so carry no dependency.
|
|
|
|
|
for (V3GraphVertex* const vtxp : m_graphp->vertices().unlinkable()) {
|
|
|
|
|
SplitVarStdVertex* const vstdp = vtxp->cast<SplitVarStdVertex>();
|
|
|
|
|
if (!vstdp || !vstdp->outEmpty()) continue;
|
|
|
|
|
UINFOTREE(9, vstdp->nodep(), "", "Will remove deps on block input var:");
|
|
|
|
|
vstdp->nodep()->user1p(nullptr); // Don't leave a dangling pointer behind
|
|
|
|
|
VL_DO_DANGLING(vstdp->unlinkDelete(m_graphp), vstdp);
|
2026-09-16 22:25:36 +02:00
|
|
|
}
|
2026-09-17 23:11:26 +02:00
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("splitg_noinputs", false);
|
|
|
|
|
|
|
|
|
|
// A statement under an 'if' also has an edge to the 'if' itself, from
|
|
|
|
|
// each variable it writes, so an 'if' holds its whole body together.
|
|
|
|
|
// An 'if' has out edges only for what its own condition reads. If
|
|
|
|
|
// after the pruning of block inputs above, an 'if' has no remaining
|
|
|
|
|
// out edges (dependencies of its condition), then it constrains
|
|
|
|
|
// nothing. If so, then remove the 'if' statement vertex, so
|
|
|
|
|
// its contents can split apart, each part taking a copy of the
|
|
|
|
|
// condition. This is what allows splitting within an if/else at all,
|
|
|
|
|
// and is what breaks up the reset tree in the example at the top of
|
|
|
|
|
// this file.
|
|
|
|
|
for (V3GraphVertex* const vtxp : m_graphp->vertices().unlinkable()) {
|
|
|
|
|
SplitStmtVertex* const stmtVtxp = vtxp->cast<SplitStmtVertex>();
|
|
|
|
|
if (!stmtVtxp || !VN_IS(stmtVtxp->nodep(), If)) continue;
|
|
|
|
|
// Can't remove if dependent on a variable written in the block
|
|
|
|
|
if (!stmtVtxp->outEmpty()) continue;
|
|
|
|
|
// Depends only on block inputs, so can be split. Remove the vertex.
|
|
|
|
|
stmtVtxp->nodep()->user1p(nullptr);
|
|
|
|
|
stmtVtxp->unlinkDelete(m_graphp);
|
|
|
|
|
}
|
|
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("splitg_nofreeifs", false);
|
|
|
|
|
|
|
|
|
|
// Weak coloring to determine what must stay together in a single block
|
|
|
|
|
const uint32_t numColors = m_graphp->weaklyConnected(&V3GraphEdge::followAlwaysTrue);
|
|
|
|
|
if (dumpGraphLevel() >= 9) m_graphp->dumpDotFilePrefixed("splitg_colored", false);
|
|
|
|
|
return numColors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Take the statements of the given list, and return them distributed into one list per color.
|
|
|
|
|
static std::vector<AstNode*> splitStatements(AstNode* stmtsp, uint32_t numColors) {
|
|
|
|
|
std::vector<AstNode*> result{numColors, nullptr};
|
|
|
|
|
for (AstNode *stmtp = stmtsp, *nextp = nullptr; stmtp; stmtp = nextp) {
|
|
|
|
|
nextp = stmtp->nextp(); // 'stmtp' is unlinked below
|
|
|
|
|
|
|
|
|
|
// Comments are dropped if the block is split
|
|
|
|
|
if (VN_IS(stmtp, Comment)) continue;
|
|
|
|
|
|
|
|
|
|
// Pick up the statement vertex, which might be nullptr after pruning during analysis
|
|
|
|
|
const SplitStmtVertex* const vtxp = stmtp->user1u().to<SplitStmtVertex*>();
|
|
|
|
|
|
|
|
|
|
// If statements are duplicated for each split branch
|
|
|
|
|
if (AstIf* const ifp = VN_CAST(stmtp, If)) {
|
|
|
|
|
const auto thens = splitStatements(ifp->thensp(), numColors);
|
|
|
|
|
const auto elses = splitStatements(ifp->elsesp(), numColors);
|
|
|
|
|
FileLine* const flp = ifp->fileline();
|
|
|
|
|
// Rebuild the 'if' for each color present in either branch
|
|
|
|
|
bool empty = true;
|
|
|
|
|
for (uint32_t color = 0; color < numColors; ++color) {
|
|
|
|
|
if (!thens[color] && !elses[color]) continue;
|
|
|
|
|
empty = false;
|
|
|
|
|
// The condition is cloned for each color. An impure condition
|
|
|
|
|
// keeps the 'if' and all it holds in one component, so just once.
|
|
|
|
|
AstIf* const clonep = new AstIf{flp, ifp->condp()->cloneTree(true),
|
|
|
|
|
thens[color], elses[color]};
|
|
|
|
|
// Preserve pragmas from unique if's so assertions work properly
|
|
|
|
|
clonep->uniquePragma(ifp->uniquePragma());
|
|
|
|
|
clonep->unique0Pragma(ifp->unique0Pragma());
|
|
|
|
|
clonep->priorityPragma(ifp->priorityPragma());
|
|
|
|
|
result[color] = AstNode::addNext(result[color], clonep);
|
|
|
|
|
}
|
|
|
|
|
// There is nothing under the 'if' to guard. If its vertex was
|
|
|
|
|
// removed as having no dependencies at all, then its
|
|
|
|
|
// condition reads only block inputs and is pure, so the whole
|
|
|
|
|
// 'if' can go. Otherwise the condition might have a side
|
|
|
|
|
// effect, so keep just the condition, evaluated as a
|
|
|
|
|
// statement, under the color of the 'if' itself.
|
|
|
|
|
if (empty && vtxp) {
|
|
|
|
|
const uint32_t color = vtxp->color();
|
|
|
|
|
AstNodeExpr* const condp = ifp->condp();
|
|
|
|
|
condp->unlinkFrBack();
|
|
|
|
|
result[color] = AstNode::addNext(result[color], new AstStmtExpr{flp, condp});
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Move the leaf into its color's list
|
|
|
|
|
const uint32_t color = vtxp->color();
|
|
|
|
|
result[color] = AstNode::addNext(result[color], stmtp->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
return result;
|
2018-02-28 06:58:41 -05:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 12:56:34 +09:00
|
|
|
// VISITORS
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstAlways* nodep) override {
|
2026-09-17 23:11:26 +02:00
|
|
|
// Skip blocks created below
|
|
|
|
|
if (nodep->user3()) return;
|
2020-04-04 08:31:14 -04:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
UASSERT_OBJ(!m_graphp, nodep, "AstAlways should not nest");
|
|
|
|
|
VL_RESTORER(m_graphp);
|
|
|
|
|
VL_RESTORER(m_impureVtxp);
|
|
|
|
|
VL_RESTORER(m_noSplitWhy);
|
|
|
|
|
VL_RESTORER(m_inDly);
|
|
|
|
|
V3Graph graph;
|
|
|
|
|
m_graphp = &graph;
|
|
|
|
|
m_impureVtxp = nullptr;
|
|
|
|
|
m_noSplitWhy = nullptr;
|
|
|
|
|
m_inDly = false;
|
|
|
|
|
UASSERT_OBJ(m_stmtStackps.empty(), nodep, "Statement stack not empty");
|
2022-03-06 12:56:34 +09:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Build the graph
|
|
|
|
|
const VNUser1InUse user1InUse;
|
|
|
|
|
const VNUser2InUse user2InUse;
|
|
|
|
|
scanBlock(nodep->stmtsp());
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// We might have to give up
|
|
|
|
|
if (m_noSplitWhy) {
|
|
|
|
|
UINFO(9, " NoSplitBlock because " << m_noSplitWhy);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-09-09 12:56:36 +01:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Color the graph to identify separable statements
|
|
|
|
|
const uint32_t numColors = colorAlwaysGraph();
|
|
|
|
|
// If the whole block is one component (or empty), then nothing to split
|
|
|
|
|
if (numColors <= 1) return;
|
2026-09-09 12:56:36 +01:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
UINFO(6, " splitting always " << nodep);
|
2026-09-09 12:56:36 +01:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Count the number of new blocks inserted into the Ast: '1 -> n' split, so 'n - 1' extra
|
|
|
|
|
m_statSplits += numColors - 1;
|
2026-09-09 12:56:36 +01:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Unpick the statements out of the original block, into one list per color
|
|
|
|
|
const auto lists = splitStatements(nodep->stmtsp(), numColors);
|
|
|
|
|
UASSERT_OBJ(lists.size() == numColors, nodep, "Inconsistent split");
|
2026-09-09 12:56:36 +01:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Whatever 'splitStatements' did not take, (comments, empty ifs) is not needed any more
|
|
|
|
|
if (AstNode* const restp = nodep->stmtsp()) {
|
|
|
|
|
VL_DO_DANGLING(restp->unlinkFrBackWithNext()->deleteTree(), restp);
|
2026-09-09 12:56:36 +01:00
|
|
|
}
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Every color has a statement in it. Reuse the original block for the
|
|
|
|
|
// first color, and add a new block after it for each of the rest.
|
|
|
|
|
UASSERT_OBJ(lists.front(), nodep, "Color with no statements");
|
|
|
|
|
nodep->addStmtsp(lists.front());
|
|
|
|
|
AstNode* lastp = nodep;
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
const VAlwaysKwd kwd = nodep->keyword();
|
|
|
|
|
for (size_t i = 1; i < numColors; ++i) {
|
|
|
|
|
AstNode* const stmtsp = lists[i];
|
|
|
|
|
UASSERT_OBJ(stmtsp, nodep, "Color with no statements");
|
|
|
|
|
AstAlways* const newp = new AstAlways{flp, kwd, nullptr, stmtsp};
|
|
|
|
|
newp->user3(1); // Do not split again
|
|
|
|
|
lastp->addNextHere(newp);
|
|
|
|
|
lastp = newp;
|
2026-09-09 12:56:36 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
void visit(AstIf* nodep) override {
|
|
|
|
|
if (!m_graphp || m_noSplitWhy) return;
|
|
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_currIfp);
|
|
|
|
|
m_currIfp = nodep;
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
2026-09-09 12:56:36 +01:00
|
|
|
}
|
2026-09-17 23:11:26 +02:00
|
|
|
scanBlock(nodep->thensp());
|
|
|
|
|
scanBlock(nodep->elsesp());
|
2026-09-09 12:56:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstExprStmt* nodep) override {
|
2026-09-17 23:11:26 +02:00
|
|
|
if (!m_graphp || m_noSplitWhy) return;
|
2026-09-09 12:56:36 +01:00
|
|
|
VL_RESTORER(m_inDly);
|
|
|
|
|
m_inDly = false;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2026-09-17 23:11:26 +02:00
|
|
|
|
2026-09-09 12:56:36 +01:00
|
|
|
void visit(AstAssignDly* nodep) override {
|
2026-09-17 23:11:26 +02:00
|
|
|
if (!m_graphp || m_noSplitWhy) return;
|
2026-09-09 12:56:36 +01:00
|
|
|
iterate(nodep->rhsp());
|
|
|
|
|
VL_RESTORER(m_inDly);
|
|
|
|
|
m_inDly = true;
|
|
|
|
|
iterate(nodep->lhsp());
|
|
|
|
|
}
|
2018-02-28 06:58:41 -05:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
void visit(AstJumpGo*) override {
|
|
|
|
|
if (!m_graphp || m_noSplitWhy) return;
|
|
|
|
|
m_noSplitWhy = "JumpGo";
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
|
|
|
|
if (!m_graphp || m_noSplitWhy) return;
|
|
|
|
|
UASSERT_OBJ(!m_stmtStackps.empty(), nodep, "Not under a statement");
|
|
|
|
|
|
|
|
|
|
// Constant lookups can be ignored
|
|
|
|
|
if (nodep->varp()->isConst()) return;
|
|
|
|
|
|
|
|
|
|
AstVarScope* const vscp = nodep->varScopep();
|
|
|
|
|
|
|
|
|
|
// SPEEDUP: We add duplicate edges, that should be fixed
|
|
|
|
|
if (m_inDly && nodep->access().isWriteOrRW()) {
|
|
|
|
|
// Delayed variable: is different from non-delayed variable, writes to it
|
|
|
|
|
// are not observable while executing this block (NBA not yet committed),
|
|
|
|
|
// so add only a write edge to a separate 'post' vertex.
|
|
|
|
|
if (!vscp->user2p()) vscp->user2p(new SplitVarPostVertex{m_graphp, vscp});
|
|
|
|
|
SplitVarPostVertex* const vpostp = vscp->user2u().to<SplitVarPostVertex*>();
|
|
|
|
|
for (SplitStmtVertex* const vtxp : m_stmtStackps) addEdge(vpostp, vtxp);
|
|
|
|
|
} else if (nodep->access().isWriteOrRW()) {
|
|
|
|
|
// Regular (non-blocking) write: Need to maintain program-flow order
|
|
|
|
|
if (!vscp->user1p()) vscp->user1p(new SplitVarStdVertex{m_graphp, vscp});
|
|
|
|
|
SplitVarStdVertex* const vstdp = vscp->user1u().to<SplitVarStdVertex*>();
|
|
|
|
|
for (SplitStmtVertex* const vtxp : m_stmtStackps) addEdge(vstdp, vtxp);
|
|
|
|
|
} else {
|
|
|
|
|
// Regular (non-blocking) read: Need to maintain program-flow order
|
|
|
|
|
if (!vscp->user1p()) vscp->user1p(new SplitVarStdVertex{m_graphp, vscp});
|
|
|
|
|
SplitVarStdVertex* const vstdp = vscp->user1u().to<SplitVarStdVertex*>();
|
|
|
|
|
for (SplitStmtVertex* const vtxp : m_stmtStackps) {
|
|
|
|
|
// If this is an if statement it only depends on refs in its
|
|
|
|
|
// own condition only (not those in its branches). For other
|
|
|
|
|
// statements, just record the referene as normal.
|
|
|
|
|
if (const AstIf* const ifp = VN_CAST(vtxp->nodep(), If)) {
|
|
|
|
|
if (ifp != m_currIfp) continue;
|
|
|
|
|
}
|
|
|
|
|
addEdge(vtxp, vstdp);
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
2018-02-28 06:58:41 -05:00
|
|
|
}
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
2026-09-16 22:25:36 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
|
|
|
|
// Outside AstAlways, just descend
|
|
|
|
|
if (!m_graphp) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
2026-09-17 23:11:26 +02:00
|
|
|
// Early exit if decided not to split
|
|
|
|
|
if (m_noSplitWhy) return;
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
UASSERT_OBJ(!m_stmtStackps.empty(), nodep, "Not under a statement");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// Timing control prevents splitting
|
|
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
m_noSplitWhy = "TimingControl";
|
2026-09-16 22:44:19 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// All impure statements must be grouped together.
|
|
|
|
|
if (!nodep->isPure()) {
|
|
|
|
|
if (!m_impureVtxp) m_impureVtxp = new SplitImpureVertex{m_graphp, nodep};
|
|
|
|
|
// One edge is enough to find the weakly connected components, but
|
|
|
|
|
// it must point at the impure vertex, so it is an out edge (input
|
|
|
|
|
// dependency) of any enclosing 'if' to prevent pruning.
|
|
|
|
|
for (SplitStmtVertex* const vtxp : m_stmtStackps) addEdge(vtxp, m_impureVtxp);
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
2026-09-17 23:11:26 +02:00
|
|
|
|
|
|
|
|
iterateChildren(nodep);
|
2026-09-16 22:44:19 +02:00
|
|
|
}
|
|
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
// CONSTRUCTORS
|
|
|
|
|
explicit SplitVisitor(AstNetlist* nodep) { iterate(nodep); }
|
|
|
|
|
~SplitVisitor() override { V3Stats::addStat("Optimizations, Split always", m_statSplits); }
|
2026-09-16 22:44:19 +02:00
|
|
|
VL_UNCOPYABLE(SplitVisitor);
|
2026-02-01 05:09:40 +00:00
|
|
|
|
2026-09-17 23:11:26 +02:00
|
|
|
public:
|
|
|
|
|
static void apply(AstNetlist* nodep) { SplitVisitor{nodep}; }
|
|
|
|
|
};
|
2026-09-16 22:44:19 +02:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
|
|
|
|
// Split class functions
|
|
|
|
|
|
2026-02-01 05:09:40 +00:00
|
|
|
void V3Split::splitAll(AstNetlist* nodep) {
|
2025-05-22 20:29:32 -04:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2026-09-17 23:11:26 +02:00
|
|
|
SplitVisitor::apply(nodep);
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("split", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|