verilator/src/V3Split.cpp
2021-01-01 10:29:54 -05:00

984 lines
39 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into separate statements to reduce temps
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Split implements two separate transformations:
// splitAlwaysAll() 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
// 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:
//
// 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
//
// ...into a separate block for each of a, b, and so on. Even though this
// requires duplicating the conditional many times, it's usually
// 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 "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Split.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include "V3Graph.h"
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>
#include <unordered_set>
//######################################################################
// Support classes
class SplitNodeVertex VL_NOT_FINAL : public V3GraphVertex {
AstNode* m_nodep;
protected:
SplitNodeVertex(V3Graph* graphp, AstNode* nodep)
: V3GraphVertex{graphp}
, m_nodep{nodep} {}
virtual ~SplitNodeVertex() override = default;
// ACCESSORS
// Do not make accessor for nodep(), It may change due to
// reordering a lower block, but we don't repair it
virtual string name() const override {
return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName();
}
virtual FileLine* fileline() const override { return nodep()->fileline(); }
public:
virtual AstNode* nodep() const { return m_nodep; }
};
class SplitPliVertex final : public SplitNodeVertex {
public:
explicit SplitPliVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
virtual ~SplitPliVertex() override = default;
virtual string name() const override { return "*PLI*"; }
virtual string dotColor() const override { return "green"; }
};
class SplitLogicVertex final : public SplitNodeVertex {
public:
SplitLogicVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
virtual ~SplitLogicVertex() override = default;
virtual string dotColor() const override { return "yellow"; }
};
class SplitVarStdVertex final : public SplitNodeVertex {
public:
SplitVarStdVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
virtual ~SplitVarStdVertex() override = default;
virtual string dotColor() const override { return "skyblue"; }
};
class SplitVarPostVertex final : public SplitNodeVertex {
public:
SplitVarPostVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {}
virtual ~SplitVarPostVertex() override = default;
virtual string name() const override { return string("POST ") + SplitNodeVertex::name(); }
virtual string dotColor() const override { return "CadetBlue"; }
};
//######################################################################
// Edge types
class SplitEdge VL_NOT_FINAL : public V3GraphEdge {
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;
SplitEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight,
bool cutable = CUTABLE)
: V3GraphEdge{graphp, fromp, top, weight, cutable} {}
virtual ~SplitEdge() override = default;
public:
// Iterator for graph functions
static void incrementStep() { ++s_stepNum; }
bool ignoreThisStep() const { return m_ignoreInStep == s_stepNum; }
void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; }
virtual bool followScoreboard() const = 0;
static bool followScoreboard(const V3GraphEdge* edgep) {
const SplitEdge* oedgep = dynamic_cast<const SplitEdge*>(edgep);
if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type");
if (oedgep->ignoreThisStep()) return false;
return oedgep->followScoreboard();
}
static bool followCyclic(const V3GraphEdge* edgep) {
const SplitEdge* oedgep = dynamic_cast<const SplitEdge*>(edgep);
if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type");
return (!oedgep->ignoreThisStep());
}
virtual string dotStyle() const override {
return ignoreThisStep() ? "dotted" : V3GraphEdge::dotStyle();
}
};
uint32_t SplitEdge::s_stepNum = 0;
class SplitPostEdge final : public SplitEdge {
public:
SplitPostEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
virtual ~SplitPostEdge() override = default;
virtual bool followScoreboard() const override { return false; }
virtual string dotColor() const override { return "khaki"; }
};
class SplitLVEdge final : public SplitEdge {
public:
SplitLVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
virtual ~SplitLVEdge() override = default;
virtual bool followScoreboard() const override { return true; }
virtual string dotColor() const override { return "yellowGreen"; }
};
class SplitRVEdge final : public SplitEdge {
public:
SplitRVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
virtual ~SplitRVEdge() override = default;
virtual bool followScoreboard() const override { return true; }
virtual string dotColor() const override { return "green"; }
};
struct SplitScorebdEdge : public SplitEdge {
public:
SplitScorebdEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL} {}
virtual ~SplitScorebdEdge() override = default;
virtual bool followScoreboard() const override { return true; }
virtual string dotColor() const override { return "blue"; }
};
struct SplitStrictEdge : public SplitEdge {
// A strict order, based on the original statement order in the graph
// The only non-cutable edge type
public:
SplitStrictEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
: SplitEdge{graphp, fromp, top, WEIGHT_NORMAL, NOT_CUTABLE} {}
virtual ~SplitStrictEdge() override = default;
virtual bool followScoreboard() const override { return true; }
virtual string dotColor() const override { return "blue"; }
};
//######################################################################
// Split class functions
class SplitReorderBaseVisitor VL_NOT_FINAL : public AstNVisitor {
private:
// NODE STATE
// AstVarScope::user1p -> Var SplitNodeVertex* for usage var, 0=not set yet
// AstVarScope::user2p -> Var SplitNodeVertex* for delayed assignment var, 0=not set yet
// Ast*::user3p -> Statement SplitLogicVertex* (temporary only)
// Ast*::user4 -> Current ordering number (reorderBlock usage)
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
AstUser4InUse m_inuser4;
protected:
// TYPES
typedef std::vector<SplitLogicVertex*> VStack;
// STATE
string m_noReorderWhy; // Reason we can't reorder
VStack m_stmtStackps; // Current statements being tracked
SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering
V3Graph m_graph; // Scoreboard of var usages/dependencies
bool m_inDly; // Inside ASSIGNDLY
VDouble0 m_statSplits; // Statistic tracking
// CONSTRUCTORS
public:
SplitReorderBaseVisitor() { scoreboardClear(); }
virtual ~SplitReorderBaseVisitor() override {
V3Stats::addStat("Optimizations, Split always", m_statSplits);
}
// METHODS
protected:
VL_DEBUG_FUNC; // Declare debug()
void scoreboardClear() {
// VV***** We reset user1p() and user2p on each block!!!
m_inDly = false;
m_graph.clear();
m_stmtStackps.clear();
m_pliVertexp = nullptr;
m_noReorderWhy = "";
AstNode::user1ClearTree();
AstNode::user2ClearTree();
AstNode::user3ClearTree();
AstNode::user4ClearTree();
}
private:
void scoreboardPli(AstNode* nodep) {
// Order all PLI statements with other PLI statements
// This ensures $display's and such remain in proper order
// We don't prevent splitting out other non-pli statements, however.
if (!m_pliVertexp) {
m_pliVertexp = new SplitPliVertex(&m_graph, nodep); // m_graph.clear() will delete it
}
for (const auto& vtxp : m_stmtStackps) {
// Both ways...
new SplitScorebdEdge(&m_graph, vtxp, m_pliVertexp);
new SplitScorebdEdge(&m_graph, m_pliVertexp, vtxp);
}
}
void scoreboardPushStmt(AstNode* nodep) {
// UINFO(9, " push " << nodep << endl);
SplitLogicVertex* vertexp = new SplitLogicVertex(&m_graph, nodep);
m_stmtStackps.push_back(vertexp);
UASSERT_OBJ(!nodep->user3p(), nodep, "user3p should not be used; cleared in processBlock");
nodep->user3p(vertexp);
}
void scoreboardPopStmt() {
// UINFO(9, " pop" << endl);
if (m_stmtStackps.empty()) v3fatalSrc("Stack underflow");
m_stmtStackps.pop_back();
}
protected:
void scanBlock(AstNode* nodep) {
// Iterate across current block, making the scoreboard
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
scoreboardPushStmt(nextp);
iterate(nextp);
scoreboardPopStmt();
}
}
void pruneDepsOnInputs() {
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
if (!vertexp->outBeginp() && dynamic_cast<SplitVarStdVertex*>(vertexp)) {
if (debug() >= 9) {
SplitVarStdVertex* stdp = static_cast<SplitVarStdVertex*>(vertexp);
UINFO(0, "Will prune deps on var " << stdp->nodep() << endl);
stdp->nodep()->dumpTree(cout, "- ");
}
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
oedgep->setIgnoreThisStep();
}
}
}
}
virtual void makeRvalueEdges(SplitVarStdVertex* vstdp) = 0;
// VISITORS
virtual void visit(AstAlways* nodep) override = 0;
virtual void visit(AstNodeIf* nodep) override = 0;
// We don't do AstNodeFor/AstWhile loops, due to the standard question
// of what is before vs. after
virtual void visit(AstAssignDly* nodep) override {
m_inDly = true;
UINFO(4, " ASSIGNDLY " << nodep << endl);
iterateChildren(nodep);
m_inDly = false;
}
virtual void visit(AstVarRef* nodep) override {
if (!m_stmtStackps.empty()) {
AstVarScope* vscp = nodep->varScopep();
UASSERT_OBJ(vscp, nodep, "Not linked");
if (!nodep->varp()->isConst()) { // Constant lookups can be ignored
// ---
// NOTE: Formerly at this location we would avoid
// splitting or reordering if the variable is public.
//
// However, it should be perfectly safe to split an
// always block containing a public variable.
// Neither operation should perturb PLI's view of
// the variable.
//
// Former code:
//
// if (nodep->varp()->isSigPublic()) {
// // Public signals shouldn't be changed,
// // pli code might be messing with them
// scoreboardPli(nodep);
// }
// ---
// Create vertexes for variable
if (!vscp->user1p()) {
SplitVarStdVertex* vstdp = new SplitVarStdVertex(&m_graph, vscp);
vscp->user1p(vstdp);
}
SplitVarStdVertex* vstdp = reinterpret_cast<SplitVarStdVertex*>(vscp->user1p());
// SPEEDUP: We add duplicate edges, that should be fixed
if (m_inDly && nodep->access().isWriteOrRW()) {
UINFO(4, " VARREFDLY: " << nodep << endl);
// Delayed variable is different from non-delayed variable
if (!vscp->user2p()) {
SplitVarPostVertex* vpostp = new SplitVarPostVertex(&m_graph, vscp);
vscp->user2p(vpostp);
new SplitPostEdge(&m_graph, vstdp, vpostp);
}
SplitVarPostVertex* vpostp
= reinterpret_cast<SplitVarPostVertex*>(vscp->user2p());
// Add edges
for (SplitLogicVertex* vxp : m_stmtStackps) {
new SplitLVEdge(&m_graph, vpostp, vxp);
}
} else { // Nondelayed assignment
if (nodep->access().isWriteOrRW()) {
// Non-delay; need to maintain existing ordering
// with all consumers of the signal
UINFO(4, " VARREFLV: " << nodep << endl);
for (SplitLogicVertex* ivxp : m_stmtStackps) {
new SplitLVEdge(&m_graph, vstdp, ivxp);
}
} else {
UINFO(4, " VARREF: " << nodep << endl);
makeRvalueEdges(vstdp);
}
}
}
}
}
virtual void visit(AstJumpGo* nodep) override {
// Jumps will disable reordering at all levels
// This is overly pessimistic; we could treat jumps as barriers, and
// reorder everything between jumps/labels, however jumps are rare
// in always, so the performance gain probably isn't worth the work.
UINFO(9, " NoReordering " << nodep << endl);
m_noReorderWhy = "JumpGo";
iterateChildren(nodep);
}
//--------------------
// Default
virtual void visit(AstNode* nodep) override {
// **** SPECIAL default type that sets PLI_ORDERING
if (!m_stmtStackps.empty() && !nodep->isPure()) {
UINFO(9, " NotSplittable " << nodep << endl);
scoreboardPli(nodep);
}
iterateChildren(nodep);
}
private:
VL_UNCOPYABLE(SplitReorderBaseVisitor);
};
class ReorderVisitor final : public SplitReorderBaseVisitor {
// CONSTRUCTORS
public:
explicit ReorderVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ReorderVisitor() override = default;
// METHODS
protected:
virtual 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 << endl);
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
if (debug() >= 9) {
m_graph.dumpDotFilePrefixed("reorderg_nodup", false);
// m_graph.dump(); cout<<endl;
}
// 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* 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* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
if (SplitLogicVertex* vvertexp = dynamic_cast<SplitLogicVertex*>(vertexp)) {
if (!vvertexp->user()) {
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep;
edgep = edgep->inNextp()) {
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
oedgep->setIgnoreThisStep();
}
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep;
edgep = edgep->outNextp()) {
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
oedgep->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* vvertexp = reinterpret_cast<SplitLogicVertex*>(nextp->user3p());
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 (debug() >= 9) m_graph.dumpDotFilePrefixed(string("splitg_preo"), false);
m_graph.acyclic(&SplitEdge::followCyclic);
m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive
if (debug() >= 9) m_graph.dumpDotFilePrefixed(string("splitg_opt"), false);
}
void reorderBlock(AstNode* nodep) {
// Reorder statements in the completed graph
// Map the rank numbers into nodes they associate with
typedef std::multimap<uint32_t, AstNode*> RankNodeMap;
RankNodeMap rankMap;
int currOrder = 0; // Existing sequence number of assignment
for (AstNode* nextp = nodep; nextp; nextp = nextp->nextp()) {
SplitLogicVertex* 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 (RankNodeMap::const_iterator it = rankMap.begin(); it != rankMap.end(); ++it) {
AstNode* nextp = it->second;
if (++newOrder != nextp->user4()) leaveAlone = false;
}
if (leaveAlone) {
UINFO(6, " No changes\n");
} else {
AstNRelinker replaceHandle; // Where to add the list
AstNode* newListp = nullptr;
for (RankNodeMap::const_iterator it = rankMap.begin(); it != rankMap.end(); ++it) {
AstNode* nextp = it->second;
UINFO(6, " New order: " << nextp << endl);
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* 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 << endl);
// Process block and followers
scanBlock(nodep);
if (m_noReorderWhy != "") { // Jump or something nasty
UINFO(9, " NoReorderBlock because " << m_noReorderWhy << endl);
} 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* vvertexp
= reinterpret_cast<SplitLogicVertex*>(nextp->user3p());
vvertexp->unlinkDelete(&m_graph);
}
}
}
// Again, nodep may no longer be first.
firstp->user3p(oldBlockUser3);
}
virtual void visit(AstAlways* nodep) override {
UINFO(4, " ALW " << nodep << endl);
if (debug() >= 9) nodep->dumpTree(cout, " alwIn:: ");
scoreboardClear();
processBlock(nodep->bodysp());
if (debug() >= 9) nodep->dumpTree(cout, " alwOut: ");
}
virtual void visit(AstNodeIf* nodep) override {
UINFO(4, " IF " << nodep << endl);
iterateAndNextNull(nodep->condp());
processBlock(nodep->ifsp());
processBlock(nodep->elsesp());
}
private:
VL_UNCOPYABLE(ReorderVisitor);
};
typedef std::unordered_set<uint32_t> ColorSet;
typedef std::vector<AstAlways*> AlwaysVec;
class IfColorVisitor final : public AstNVisitor {
// MEMBERS
ColorSet m_colors; // All colors in the original always block
typedef std::vector<AstNodeIf*> IfStack;
IfStack m_ifStack; // Stack of nested if-statements we're currently processing
typedef std::unordered_map<AstNodeIf*, ColorSet> IfColorMap;
IfColorMap m_ifColors; // Map each if-statement to the set of colors (split blocks)
// that will get a copy of that if-statement
// CONSTRUCTORS
public:
// Visit through *nodep and map each AstNodeIf within to the set of
// colors it will participate in. Also find the whole set of colors.
explicit IfColorVisitor(AstAlways* nodep) { iterate(nodep); }
virtual ~IfColorVisitor() override = default;
// METHODS
const ColorSet& colors() const { return m_colors; }
const ColorSet& colors(AstNodeIf* nodep) const {
const auto it = m_ifColors.find(nodep);
UASSERT_OBJ(it != m_ifColors.end(), nodep, "Node missing from split color() map");
return it->second;
}
private:
void trackNode(AstNode* nodep) {
if (nodep->user3p()) {
SplitLogicVertex* vertexp = reinterpret_cast<SplitLogicVertex*>(nodep->user3p());
uint32_t color = vertexp->color();
m_colors.insert(color);
UINFO(8, " SVL " << vertexp << " has color " << color << "\n");
// Record that all containing ifs have this color.
for (IfStack::const_iterator it = m_ifStack.begin(); it != m_ifStack.end(); ++it) {
m_ifColors[*it].insert(color);
}
}
}
protected:
virtual void visit(AstNodeIf* nodep) override {
m_ifStack.push_back(nodep);
trackNode(nodep);
iterateChildren(nodep);
m_ifStack.pop_back();
}
virtual void visit(AstNode* nodep) override {
trackNode(nodep);
iterateChildren(nodep);
}
VL_DEBUG_FUNC; // Declare debug()
private:
VL_UNCOPYABLE(IfColorVisitor);
};
class EmitSplitVisitor final : public AstNVisitor {
// MEMBERS
AstAlways* m_origAlwaysp; // Block that *this will split
const IfColorVisitor* m_ifColorp; // Digest of results of prior coloring
// Map each color to our current place within the color's new always
typedef std::unordered_map<uint32_t, AstNode*> LocMap;
LocMap m_addAfter;
AlwaysVec* m_newBlocksp; // Split always blocks we have generated
// CONSTRUCTORS
public:
// EmitSplitVisitor visits through always block *nodep
// and generates its split blocks, writing the split blocks
// into *newBlocksp.
EmitSplitVisitor(AstAlways* nodep, const IfColorVisitor* ifColorp, AlwaysVec* newBlocksp)
: m_origAlwaysp{nodep}
, m_ifColorp{ifColorp}
, m_newBlocksp{newBlocksp} {
UINFO(6, " splitting always " << nodep << endl);
}
virtual ~EmitSplitVisitor() override = default;
// METHODS
void go() {
// Create a new always for each color
const ColorSet& colors = m_ifColorp->colors();
for (unsigned int color : colors) {
// We don't need to clone m_origAlwaysp->sensesp() here;
// V3Activate already moved it to a parent node.
AstAlways* alwaysp
= new AstAlways(m_origAlwaysp->fileline(), VAlwaysKwd::ALWAYS, nullptr, nullptr);
// Put a placeholder node into stmtp to track our position.
// We'll strip these out after the blocks are fully cloned.
AstSplitPlaceholder* placeholderp = makePlaceholderp();
alwaysp->addStmtp(placeholderp);
m_addAfter[color] = placeholderp;
m_newBlocksp->push_back(alwaysp);
}
// Scan the body of the always. We'll handle if/else
// specially, everything else is a leaf node that we can
// just clone into one of the split always blocks.
iterateAndNextNull(m_origAlwaysp->bodysp());
}
protected:
VL_DEBUG_FUNC; // Declare debug()
AstSplitPlaceholder* makePlaceholderp() {
return new AstSplitPlaceholder(m_origAlwaysp->fileline());
}
virtual void visit(AstNode* nodep) override {
// Anything that's not an if/else we assume is a leaf
// (that is, something we won't split.) Don't visit further
// into the leaf.
//
// A leaf might contain another if, for example a WHILE loop
// could contain an if. We can't split WHILE loops, so we
// won't split its nested if either. Just treat it as part
// of the leaf; do not visit further; do not reach visit(AstNodeIf*)
// for such an embedded if.
// Each leaf must have a user3p
UASSERT_OBJ(nodep->user3p(), nodep, "null user3p in V3Split leaf");
// Clone the leaf into its new always block
SplitLogicVertex* vxp = reinterpret_cast<SplitLogicVertex*>(nodep->user3p());
uint32_t color = vxp->color();
AstNode* clonedp = nodep->cloneTree(false);
m_addAfter[color]->addNextHere(clonedp);
m_addAfter[color] = clonedp;
}
virtual void visit(AstNodeIf* nodep) override {
const ColorSet& colors = m_ifColorp->colors(nodep);
typedef std::unordered_map<uint32_t, AstNodeIf*> CloneMap;
CloneMap clones;
for (unsigned int color : colors) {
// Clone this if into its set of split blocks
AstSplitPlaceholder* if_placeholderp = makePlaceholderp();
AstSplitPlaceholder* else_placeholderp = makePlaceholderp();
AstIf* clonep = new AstIf(nodep->fileline(), nodep->condp()->cloneTree(true),
if_placeholderp, else_placeholderp);
AstIf* origp = VN_CAST(nodep, If);
if (origp) {
// Preserve pragmas from unique if's
// so assertions work properly
clonep->uniquePragma(origp->uniquePragma());
clonep->unique0Pragma(origp->unique0Pragma());
clonep->priorityPragma(origp->priorityPragma());
}
clones[color] = clonep;
m_addAfter[color]->addNextHere(clonep);
m_addAfter[color] = if_placeholderp;
}
iterateAndNextNull(nodep->ifsp());
for (const auto& color : colors) m_addAfter[color] = clones[color]->elsesp();
iterateAndNextNull(nodep->elsesp());
for (const auto& color : colors) m_addAfter[color] = clones[color];
}
private:
VL_UNCOPYABLE(EmitSplitVisitor);
};
class RemovePlaceholdersVisitor final : public AstNVisitor {
typedef std::unordered_set<AstNode*> NodeSet;
NodeSet m_removeSet; // placeholders to be removed
public:
explicit RemovePlaceholdersVisitor(AstNode* nodep) {
iterate(nodep);
for (AstNode* np : m_removeSet) {
np->unlinkFrBack(); // Without next
VL_DO_DANGLING(np->deleteTree(), np);
}
}
virtual ~RemovePlaceholdersVisitor() override = default;
virtual void visit(AstSplitPlaceholder* nodep) override { m_removeSet.insert(nodep); }
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
private:
VL_UNCOPYABLE(RemovePlaceholdersVisitor);
};
class SplitVisitor final : public SplitReorderBaseVisitor {
private:
// Keys are original always blocks pending delete,
// values are newly split always blocks pending insertion
// at the same position as the originals:
typedef std::unordered_map<AstAlways*, AlwaysVec> ReplaceMap;
ReplaceMap m_replaceBlocks;
// AstNodeIf* whose condition we're currently visiting
AstNode* m_curIfConditional = nullptr;
// CONSTRUCTORS
public:
explicit SplitVisitor(AstNetlist* nodep) {
iterate(nodep);
// Splice newly-split blocks into the tree. Remove placeholders
// from newly-split blocks. Delete the original always blocks
// that we're replacing.
for (ReplaceMap::iterator it = m_replaceBlocks.begin(); it != m_replaceBlocks.end();
++it) {
AstAlways* origp = it->first;
for (AlwaysVec::iterator addme = it->second.begin(); addme != it->second.end();
++addme) {
origp->addNextHere(*addme);
RemovePlaceholdersVisitor removePlaceholders(*addme);
}
origp->unlinkFrBack(); // Without next
VL_DO_DANGLING(origp->deleteTree(), origp);
}
}
virtual ~SplitVisitor() override = default;
// METHODS
protected:
virtual void makeRvalueEdges(SplitVarStdVertex* vstdp) override {
// Each 'if' depends on rvalues in its own conditional ONLY,
// not rvalues in the if/else bodies.
for (VStack::const_iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) {
AstNodeIf* ifNodep = VN_CAST((*it)->nodep(), NodeIf);
if (ifNodep && (m_curIfConditional != ifNodep)) continue;
new SplitRVEdge(&m_graph, *it, vstdp);
}
}
void colorAlwaysGraph() {
// Color the graph to indicate subsets, each of which
// we can split into its own always block.
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
// Some vars are primary inputs to the always block; prune
// edges on those vars. Reasoning: if two statements both depend
// on primary input A, it's ok to split these statements. Whereas
// if they both depend on locally-generated variable B, the statements
// must be kept together.
SplitEdge::incrementStep();
pruneDepsOnInputs();
// For any 'if' node whose deps have all been pruned
// (meaning, its conditional expression only looks at primary
// inputs) prune all edges that depend on the 'if'.
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
SplitLogicVertex* logicp = dynamic_cast<SplitLogicVertex*>(vertexp);
if (!logicp) continue;
AstNodeIf* ifNodep = VN_CAST(logicp->nodep(), NodeIf);
if (!ifNodep) continue;
bool pruneMe = true;
for (V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) {
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
if (!oedgep->ignoreThisStep()) {
// This if conditional depends on something we can't
// prune -- a variable generated in the current block.
pruneMe = false;
// When we can't prune dependencies on the conditional,
// give a hint about why...
if (debug() >= 9) {
V3GraphVertex* vxp = oedgep->top();
SplitNodeVertex* nvxp = dynamic_cast<SplitNodeVertex*>(vxp);
UINFO(0, "Cannot prune if-node due to edge "
<< oedgep << " pointing to node " << nvxp->nodep() << endl);
nvxp->nodep()->dumpTree(cout, "- ");
}
break;
}
}
if (!pruneMe) continue;
// This if can be split; prune dependencies on it.
for (V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) {
SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep);
oedgep->setIgnoreThisStep();
}
}
if (debug() >= 9) m_graph.dumpDotFilePrefixed("splitg_nodup", false);
// Weak coloring to determine what needs to remain grouped
// in a single always. This follows all edges excluding:
// - those we pruned above
// - PostEdges, which are done later
m_graph.weaklyConnected(&SplitEdge::followScoreboard);
if (debug() >= 9) m_graph.dumpDotFilePrefixed("splitg_colored", false);
}
virtual void visit(AstAlways* nodep) override {
// build the scoreboard
scoreboardClear();
scanBlock(nodep->bodysp());
if (m_noReorderWhy != "") {
// We saw a jump or something else rare that we don't handle.
UINFO(9, " NoSplitBlock because " << m_noReorderWhy << endl);
return;
}
// Look across the entire tree of if/else blocks in the always,
// and color regions that must be kept together.
UINFO(5, "SplitVisitor @ " << nodep << endl);
colorAlwaysGraph();
// Map each AstNodeIf to the set of colors (split always blocks)
// it must participate in. Also find the whole set of colors.
IfColorVisitor ifColor(nodep);
if (ifColor.colors().size() > 1) {
// Counting original always blocks rather than newly-split
// always blocks makes it a little easier to use this stat to
// check the result of the t_alw_split test:
++m_statSplits;
// Visit through the original always block one more time,
// and emit the split always blocks into m_replaceBlocks:
EmitSplitVisitor emitSplit(nodep, &ifColor, &(m_replaceBlocks[nodep]));
emitSplit.go();
}
}
virtual void visit(AstNodeIf* nodep) override {
UINFO(4, " IF " << nodep << endl);
m_curIfConditional = nodep;
iterateAndNextNull(nodep->condp());
m_curIfConditional = nullptr;
scanBlock(nodep->ifsp());
scanBlock(nodep->elsesp());
}
private:
VL_UNCOPYABLE(SplitVisitor);
};
//######################################################################
// Split class functions
void V3Split::splitReorderAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ReorderVisitor visitor(nodep); } // Destruct before checking
V3Global::dumpCheckGlobalTree("reorder", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Split::splitAlwaysAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ SplitVisitor visitor(nodep); } // Destruct before checking
V3Global::dumpCheckGlobalTree("split", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}