2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Lifelicate variable assignment elimination
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LIFE TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Build control-flow graph with assignments and var usages
|
|
|
|
|
// All modules:
|
|
|
|
|
// ASSIGN(x,...), ASSIGN(x,...) => delete first one
|
|
|
|
|
// We also track across if statements:
|
|
|
|
|
// ASSIGN(X,...) IF( ..., ASSIGN(X,...), ASSIGN(X,...)) => deletes first
|
|
|
|
|
// We don't do the opposite yet though (remove assigns in if followed by outside if)
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Life.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-09-26 17:05:35 +02:00
|
|
|
#include "V3Const.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Stats.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2006-09-26 17:05:35 +02:00
|
|
|
// Structure for global state
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class LifeState final {
|
2008-11-21 21:50:33 +01:00
|
|
|
// NODE STATE
|
|
|
|
|
// See below
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2008-11-21 21:50:33 +01:00
|
|
|
|
2006-09-26 17:05:35 +02:00
|
|
|
// STATE
|
|
|
|
|
public:
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 m_statAssnDel; // Statistic tracking
|
|
|
|
|
VDouble0 m_statAssnCon; // Statistic tracking
|
2025-02-26 04:48:53 +01:00
|
|
|
VDouble0 m_statCResetDel; // Statistic tracking
|
2006-09-26 17:05:35 +02:00
|
|
|
|
2009-10-14 14:26:30 +02:00
|
|
|
// CONSTRUCTORS
|
2020-11-17 01:56:16 +01:00
|
|
|
LifeState() = default;
|
2006-09-26 17:05:35 +02:00
|
|
|
~LifeState() {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
2025-02-26 04:48:53 +01:00
|
|
|
V3Stats::addStatSum("Optimizations, Lifetime creset deletions", m_statCResetDel);
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Structure for each variable encountered
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class LifeVarEntry final {
|
2020-11-11 03:40:14 +01:00
|
|
|
// Last assignment to this varscope, nullptr if no longer relevant
|
2025-02-26 04:48:53 +01:00
|
|
|
AstNodeStmt* m_assignp = nullptr;
|
2020-11-11 03:40:14 +01:00
|
|
|
AstConst* m_constp = nullptr; // Known constant value
|
2025-10-01 16:01:30 +02:00
|
|
|
bool m_isNew = true; // Is just created
|
2020-04-15 13:58:34 +02:00
|
|
|
// First access was a set (and thus block above may have a set that can be deleted
|
2025-10-01 16:01:30 +02:00
|
|
|
bool m_setBeforeUse = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
// Was ever assigned (and thus above block may not preserve constant propagation)
|
2020-11-11 03:40:14 +01:00
|
|
|
bool m_everSet = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeVarEntry() = default;
|
|
|
|
|
~LifeVarEntry() = default;
|
2006-09-26 17:05:35 +02:00
|
|
|
|
2025-10-01 16:01:30 +02:00
|
|
|
void init(bool setBeforeUse) {
|
|
|
|
|
UASSERT(m_isNew, "Not a new entry");
|
|
|
|
|
m_isNew = false;
|
|
|
|
|
m_setBeforeUse = setBeforeUse;
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
2025-10-01 16:01:30 +02:00
|
|
|
|
2025-02-26 04:48:53 +01:00
|
|
|
void simpleAssign(AstNodeAssign* nodep) { // New simple A=.... assignment
|
2025-10-01 16:01:30 +02:00
|
|
|
UASSERT_OBJ(!m_isNew, nodep, "Uninitialzized new entry");
|
2025-02-26 04:48:53 +01:00
|
|
|
m_assignp = nodep;
|
|
|
|
|
m_constp = nullptr;
|
|
|
|
|
m_everSet = true;
|
|
|
|
|
if (VN_IS(nodep->rhsp(), Const)) m_constp = VN_AS(nodep->rhsp(), Const);
|
|
|
|
|
}
|
|
|
|
|
void resetStatement(AstCReset* nodep) { // New CReset(A) assignment
|
2025-10-01 16:01:30 +02:00
|
|
|
UASSERT_OBJ(!m_isNew, nodep, "Uninitialzized new entry");
|
2025-02-26 04:48:53 +01:00
|
|
|
m_assignp = nodep;
|
2020-08-15 16:12:55 +02:00
|
|
|
m_constp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_everSet = true;
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
void complexAssign() { // A[x]=... or some complicated assignment
|
2025-10-01 16:01:30 +02:00
|
|
|
UASSERT(!m_isNew, "Uninitialzized new entry");
|
2020-08-15 16:12:55 +02:00
|
|
|
m_assignp = nullptr;
|
|
|
|
|
m_constp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_everSet = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
void consumed() { // Rvalue read of A
|
2025-10-01 16:01:30 +02:00
|
|
|
UASSERT(!m_isNew, "Uninitialzized new entry");
|
2020-08-15 16:12:55 +02:00
|
|
|
m_assignp = nullptr;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-02-26 04:48:53 +01:00
|
|
|
AstNodeStmt* assignp() const { return m_assignp; }
|
2006-09-26 17:05:35 +02:00
|
|
|
AstConst* constNodep() const { return m_constp; }
|
2025-10-01 16:01:30 +02:00
|
|
|
bool isNew() const { return m_isNew; }
|
2006-09-26 17:05:35 +02:00
|
|
|
bool setBeforeUse() const { return m_setBeforeUse; }
|
|
|
|
|
bool everSet() const { return m_everSet; }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2006-09-26 17:05:35 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Structure for all variables under a given meta-basic block
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class LifeBlock final {
|
2006-09-26 17:05:35 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared each AstIf:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVarScope::user1() -> int. Used in combining to detect duplicates
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// LIFE MAP
|
2025-10-01 16:01:30 +02:00
|
|
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
|
|
|
|
// Current active lifetime map for current scope
|
|
|
|
|
std::unordered_map<AstVarScope*, LifeVarEntry> m_map;
|
2022-01-01 17:46:49 +01:00
|
|
|
LifeBlock* const m_aboveLifep; // Upper life, or nullptr
|
|
|
|
|
LifeState* const m_statep; // Current global state
|
2022-09-24 21:44:00 +02:00
|
|
|
bool m_replacedVref = false; // Replaced a variable reference since last clearing
|
2024-08-14 09:23:24 +02:00
|
|
|
VNDeleter m_deleter; // Used to delay deletion of nodes
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2006-09-26 17:05:35 +02:00
|
|
|
public:
|
2022-01-01 17:46:49 +01:00
|
|
|
LifeBlock(LifeBlock* aboveLifep, LifeState* statep)
|
|
|
|
|
: m_aboveLifep{aboveLifep} // Null if top
|
|
|
|
|
, m_statep{statep} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
~LifeBlock() = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2025-11-13 00:54:22 +01:00
|
|
|
void checkRemoveAssign(const AstVarScope* vscp, LifeVarEntry& entr) {
|
2025-10-01 16:01:30 +02:00
|
|
|
const AstVar* const varp = vscp->varp();
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
// We don't optimize any public sigs
|
|
|
|
|
if (varp->isSigPublic()) return;
|
|
|
|
|
if (varp->sensIfacep()) return;
|
|
|
|
|
// Check the var entry, and remove if appropriate
|
|
|
|
|
AstNodeStmt* const oldassp = entr.assignp();
|
|
|
|
|
if (!oldassp) return;
|
|
|
|
|
UINFO(7, " PREV: " << oldassp);
|
|
|
|
|
// Redundant assignment, in same level block
|
|
|
|
|
// Don't delete it now as it will confuse iteration since it maybe WAY
|
|
|
|
|
// above our current iteration point.
|
|
|
|
|
UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK");
|
|
|
|
|
entr.complexAssign();
|
|
|
|
|
oldassp->unlinkFrBack();
|
|
|
|
|
if (VN_IS(oldassp, CReset)) {
|
|
|
|
|
++m_statep->m_statCResetDel;
|
|
|
|
|
} else {
|
|
|
|
|
++m_statep->m_statAssnDel;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
VL_DO_DANGLING(m_deleter.pushDeletep(oldassp), oldassp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-02-26 04:48:53 +01:00
|
|
|
void resetStatement(AstVarScope* nodep, AstCReset* rstp) {
|
|
|
|
|
// Do we have a old assignment we can nuke?
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " CRESETof: " << nodep);
|
|
|
|
|
UINFO(7, " new: " << rstp);
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeVarEntry& entr = m_map[nodep];
|
|
|
|
|
if (entr.isNew()) {
|
|
|
|
|
entr.init(true);
|
|
|
|
|
} else {
|
|
|
|
|
checkRemoveAssign(nodep, entr);
|
2025-02-26 04:48:53 +01:00
|
|
|
}
|
2025-10-01 16:01:30 +02:00
|
|
|
entr.resetStatement(rstp);
|
2025-02-26 04:48:53 +01:00
|
|
|
// lifeDump();
|
|
|
|
|
}
|
2006-09-26 17:05:35 +02:00
|
|
|
void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Do we have a old assignment we can nuke?
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " ASSIGNof: " << nodep);
|
|
|
|
|
UINFO(7, " new: " << assp);
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeVarEntry& entr = m_map[nodep];
|
|
|
|
|
if (entr.isNew()) {
|
|
|
|
|
entr.init(true);
|
|
|
|
|
} else {
|
|
|
|
|
checkRemoveAssign(nodep, entr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-10-01 16:01:30 +02:00
|
|
|
entr.simpleAssign(assp);
|
2020-04-15 13:58:34 +02:00
|
|
|
// lifeDump();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2006-09-26 17:05:35 +02:00
|
|
|
void complexAssign(AstVarScope* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " clearof: " << nodep);
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeVarEntry& entr = m_map[nodep];
|
|
|
|
|
if (entr.isNew()) entr.init(false);
|
|
|
|
|
entr.complexAssign();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-24 21:44:00 +02:00
|
|
|
void clearReplaced() { m_replacedVref = false; }
|
|
|
|
|
bool replaced() const { return m_replacedVref; }
|
2018-08-25 15:52:45 +02:00
|
|
|
void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) {
|
2022-03-31 02:17:59 +02:00
|
|
|
// Variable rvalue. If it references a constant, we can replace it
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeVarEntry& entr = m_map[nodep];
|
|
|
|
|
if (entr.isNew()) {
|
|
|
|
|
entr.init(false);
|
|
|
|
|
} else {
|
|
|
|
|
if (AstConst* const constp = entr.constNodep()) {
|
2023-12-05 04:11:07 +01:00
|
|
|
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->sensIfacep()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Aha, variable is constant; substitute in.
|
|
|
|
|
// We'll later constant propagate
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " replaceconst: " << varrefp);
|
2019-05-19 22:13:13 +02:00
|
|
|
varrefp->replaceWith(constp->cloneTree(false));
|
2022-09-24 21:44:00 +02:00
|
|
|
m_replacedVref = true;
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(varrefp->deleteTree(), varrefp);
|
2019-05-19 22:13:13 +02:00
|
|
|
++m_statep->m_statAssnCon;
|
|
|
|
|
return; // **DONE, no longer a var reference**
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " usage: " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-10-01 16:01:30 +02:00
|
|
|
entr.consumed();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2006-09-26 17:05:35 +02:00
|
|
|
void complexAssignFind(AstVarScope* nodep) {
|
2025-10-01 16:01:30 +02:00
|
|
|
UINFO(4, " casfind: " << nodep);
|
|
|
|
|
LifeVarEntry& entr = m_map[nodep];
|
|
|
|
|
if (entr.isNew()) entr.init(false);
|
|
|
|
|
entr.complexAssign();
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
|
|
|
|
void consumedFind(AstVarScope* nodep) {
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeVarEntry& entr = m_map[nodep];
|
|
|
|
|
if (entr.isNew()) entr.init(false);
|
|
|
|
|
entr.consumed();
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
|
|
|
|
void lifeToAbove() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
2024-01-06 00:00:06 +01:00
|
|
|
UASSERT(m_aboveLifep, "Pushing life when already at the top level");
|
2022-06-16 00:49:32 +02:00
|
|
|
for (auto& itr : m_map) {
|
|
|
|
|
AstVarScope* const nodep = itr.first;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_aboveLifep->complexAssignFind(nodep);
|
2022-06-16 00:49:32 +02:00
|
|
|
if (itr.second.everSet()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Record there may be an assignment, so we don't constant propagate across the if.
|
|
|
|
|
complexAssignFind(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
// Record consumption, so we don't eliminate earlier assignments
|
|
|
|
|
consumedFind(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void dualBranch(LifeBlock* life1p, LifeBlock* life2p) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Find any common sets on both branches of IF and propagate upwards
|
2020-04-15 13:58:34 +02:00
|
|
|
// life1p->lifeDump();
|
|
|
|
|
// life2p->lifeDump();
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode::user1ClearTree(); // user1p() used on entire tree
|
2022-06-16 00:49:32 +02:00
|
|
|
for (auto& itr : life1p->m_map) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// When the if branch sets a var before it's used, mark that variable
|
2022-06-16 00:49:32 +02:00
|
|
|
if (itr.second.setBeforeUse()) itr.first->user1(1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-06-16 00:49:32 +02:00
|
|
|
for (auto& itr : life2p->m_map) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// When the else branch sets a var before it's used
|
2022-06-16 00:49:32 +02:00
|
|
|
AstVarScope* const nodep = itr.first;
|
|
|
|
|
if (itr.second.setBeforeUse() && nodep->user1()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Both branches set the var, we can remove the assignment before the IF.
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, "DUALBRANCH " << nodep);
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto itab = m_map.find(nodep);
|
2025-10-01 16:01:30 +02:00
|
|
|
if (itab != m_map.end()) checkRemoveAssign(nodep, itab->second);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
// this->lifeDump();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-06-16 00:11:03 +02:00
|
|
|
void clear() { m_map.clear(); }
|
2006-09-26 17:05:35 +02:00
|
|
|
// DEBUG
|
|
|
|
|
void lifeDump() {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " LifeMap:");
|
2022-06-16 00:49:32 +02:00
|
|
|
for (const auto& itr : m_map) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5,
|
|
|
|
|
" Ent: " << (itr.second.setBeforeUse() ? "[F] " : " ") << itr.first);
|
2022-06-16 00:49:32 +02:00
|
|
|
if (itr.second.assignp()) { //
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " Ass: " << itr.second.assignp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2006-09-26 17:05:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Life state, as a visitor of each AstNode
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class LifeVisitor final : public VNVisitor {
|
2006-09-26 17:05:35 +02:00
|
|
|
// STATE
|
2022-01-01 17:46:49 +01:00
|
|
|
LifeState* const m_statep; // Current state
|
2025-10-25 21:59:21 +02:00
|
|
|
bool m_containsTiming = false; // Statement contains timing control
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
|
|
|
|
bool m_noopt = false; // Disable optimization of variables in this block
|
|
|
|
|
bool m_tracingCall = false; // Iterating into a CCall to a CFunc
|
2025-10-01 16:01:30 +02:00
|
|
|
LifeBlock* m_lifep = nullptr; // Current active lifetime map for current scope
|
2006-09-26 17:05:35 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2022-06-16 00:11:03 +02:00
|
|
|
void setNoopt() {
|
|
|
|
|
m_noopt = true;
|
|
|
|
|
m_lifep->clear();
|
|
|
|
|
}
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-09-26 17:05:35 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Consumption/generation of a variable,
|
|
|
|
|
// it's used so can't elim assignment before this use.
|
2020-08-15 16:12:55 +02:00
|
|
|
UASSERT_OBJ(nodep->varScopep(), nodep, "nullptr");
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const vscp = nodep->varScopep();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isWriteOrRW()) {
|
2019-07-06 02:09:56 +02:00
|
|
|
m_sideEffect = true; // $sscanf etc may have RHS vars that are lvalues
|
2019-05-19 22:13:13 +02:00
|
|
|
m_lifep->complexAssign(vscp);
|
|
|
|
|
} else {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(m_lifep->varUsageReplace(vscp, nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2025-01-28 13:30:40 +01:00
|
|
|
if (nodep->isTimingControl() || VN_IS(nodep, AssignForce)) {
|
|
|
|
|
// V3Life doesn't understand time sense nor force assigns - don't optimize
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
setNoopt();
|
2025-10-25 21:59:21 +02:00
|
|
|
if (nodep->isTimingControl()) m_containsTiming = true;
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// Collect any used variables first, as lhs may also be on rhs
|
|
|
|
|
// Similar code in V3Dead
|
2023-11-12 13:32:08 +01:00
|
|
|
VL_RESTORER(m_sideEffect);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_sideEffect = false;
|
2022-09-24 21:44:00 +02:00
|
|
|
m_lifep->clearReplaced();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->rhsp());
|
2022-09-24 21:44:00 +02:00
|
|
|
if (m_lifep->replaced()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We changed something, try to constant propagate, but don't delete the
|
|
|
|
|
// assignment as we still need nodep to remain.
|
|
|
|
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
|
|
|
|
}
|
|
|
|
|
// Has to be direct assignment without any EXTRACTing.
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarScope* const vscp = VN_AS(nodep->lhsp(), VarRef)->varScopep();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(vscp, nodep, "Scope lost on variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_lifep->simpleAssign(vscp, nodep);
|
|
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-02-26 04:48:53 +01:00
|
|
|
void visit(AstCReset* nodep) override {
|
|
|
|
|
if (!m_noopt) {
|
|
|
|
|
AstVarScope* const vscp = nodep->varrefp()->varScopep();
|
|
|
|
|
UASSERT_OBJ(vscp, nodep, "Scope lost on variable");
|
|
|
|
|
m_lifep->resetStatement(vscp, nodep);
|
|
|
|
|
} else {
|
|
|
|
|
iterateAndNextNull(nodep->varrefp());
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignDly* nodep) override {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
// V3Life doesn't understand time sense
|
|
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
// Don't optimize
|
|
|
|
|
setNoopt();
|
2025-10-25 21:59:21 +02:00
|
|
|
m_containsTiming = true;
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
}
|
|
|
|
|
// Don't treat as normal assign
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2008-06-10 03:25:10 +02:00
|
|
|
//---- Track control flow changes
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " IF " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Condition is part of PREVIOUS block
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->condp());
|
2021-11-13 19:50:44 +01:00
|
|
|
LifeBlock* const prevLifep = m_lifep;
|
2022-11-20 19:11:01 +01:00
|
|
|
LifeBlock* const ifLifep = new LifeBlock{prevLifep, m_statep};
|
|
|
|
|
LifeBlock* const elseLifep = new LifeBlock{prevLifep, m_statep};
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
|
|
|
|
m_lifep = ifLifep;
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->thensp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
m_lifep = elseLifep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->elsesp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2019-10-25 03:48:45 +02:00
|
|
|
m_lifep = prevLifep;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " join ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Find sets on both flows
|
2018-08-25 15:52:45 +02:00
|
|
|
m_lifep->dualBranch(ifLifep, elseLifep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
ifLifep->lifeToAbove();
|
|
|
|
|
elseLifep->lifeToAbove();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(delete ifLifep, ifLifep);
|
|
|
|
|
VL_DO_DANGLING(delete elseLifep, elseLifep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstLoop* nodep) override {
|
|
|
|
|
// Similar problem to AstJumpBlock, don't optimize loop bodies - most are unrolled
|
|
|
|
|
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
2025-10-25 21:59:21 +02:00
|
|
|
VL_RESTORER(m_containsTiming);
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
2025-09-29 16:25:25 +02:00
|
|
|
VL_RESTORER(m_noopt);
|
2025-10-25 21:59:21 +02:00
|
|
|
VL_RESTORER(m_lifep);
|
|
|
|
|
m_lifep = new LifeBlock{m_lifep, m_statep};
|
2025-09-29 16:25:25 +02:00
|
|
|
setNoopt();
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2025-10-25 21:59:21 +02:00
|
|
|
UINFO(4, " joinloop");
|
|
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
m_lifep->lifeToAbove();
|
|
|
|
|
VL_DO_DANGLING(delete m_lifep, m_lifep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-10-25 21:59:21 +02:00
|
|
|
if (m_containsTiming) setNoopt();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpBlock* nodep) override {
|
2025-09-29 16:25:25 +02:00
|
|
|
// As with Loop's we can't predict if a JumpGo will kill us or not
|
2019-05-19 22:13:13 +02:00
|
|
|
// It's worse though as an IF(..., JUMPGO) may change the control flow.
|
|
|
|
|
// Just don't optimize blocks with labels; they're rare - so far.
|
2025-10-25 21:59:21 +02:00
|
|
|
VL_RESTORER(m_containsTiming);
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
2022-06-16 00:49:32 +02:00
|
|
|
VL_RESTORER(m_noopt);
|
2025-10-25 21:59:21 +02:00
|
|
|
VL_RESTORER(m_lifep);
|
|
|
|
|
m_lifep = new LifeBlock{m_lifep, m_statep};
|
2022-06-16 00:11:03 +02:00
|
|
|
setNoopt();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2025-10-25 21:59:21 +02:00
|
|
|
UINFO(4, " joinjump");
|
|
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
m_lifep->lifeToAbove();
|
|
|
|
|
VL_DO_DANGLING(delete m_lifep, m_lifep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-10-25 21:59:21 +02:00
|
|
|
if (m_containsTiming) setNoopt();
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCCall* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(4, " CCALL " << nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Enter the function and trace it
|
2020-04-15 13:58:34 +02:00
|
|
|
// else is non-inline or public function we optimize separately
|
2023-11-12 13:32:08 +01:00
|
|
|
if (nodep->funcp()->entryPoint()) {
|
|
|
|
|
setNoopt();
|
|
|
|
|
} else {
|
2017-11-29 00:38:19 +01:00
|
|
|
m_tracingCall = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep->funcp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(4, " CFUNC " << nodep);
|
2017-11-29 00:38:19 +01:00
|
|
|
if (!m_tracingCall && !nodep->entryPoint()) return;
|
|
|
|
|
m_tracingCall = false;
|
2023-11-12 13:32:08 +01:00
|
|
|
if (nodep->recursive()) setNoopt();
|
2023-09-08 08:51:19 +02:00
|
|
|
if (nodep->dpiImportPrototype() && !nodep->dpiPure()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2009-12-03 12:55:29 +01:00
|
|
|
}
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
void visit(AstCExprUser* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-09-26 19:05:08 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstCExpr* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2009-12-03 12:55:29 +01:00
|
|
|
}
|
2006-09-26 17:05:35 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar*) override {} // Don't want varrefs under it
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
// V3Life doesn't understand time sense - don't optimize
|
|
|
|
|
setNoopt();
|
2025-10-25 21:59:21 +02:00
|
|
|
m_containsTiming = true;
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2022-01-01 17:46:49 +01:00
|
|
|
LifeVisitor(AstNode* nodep, LifeState* statep)
|
|
|
|
|
: m_statep{statep} {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " LifeVisitor on " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
2022-11-20 19:11:01 +01:00
|
|
|
m_lifep = new LifeBlock{nullptr, m_statep};
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
if (m_lifep) VL_DO_CLEAR(delete m_lifep, m_lifep = nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~LifeVisitor() override {
|
2020-08-15 16:12:55 +02:00
|
|
|
if (m_lifep) VL_DO_CLEAR(delete m_lifep, m_lifep = nullptr);
|
2015-10-04 04:33:06 +02:00
|
|
|
}
|
2020-01-09 01:33:47 +01:00
|
|
|
VL_UNCOPYABLE(LifeVisitor);
|
2006-09-26 17:05:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class LifeTopVisitor final : public VNVisitor {
|
2006-09-26 17:05:35 +02:00
|
|
|
// Visit all top nodes searching for functions that are entry points we want to start
|
|
|
|
|
// finding code within.
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
2021-11-13 19:50:44 +01:00
|
|
|
LifeState* const m_statep; // Current state
|
2006-09-26 17:05:35 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->entryPoint()) {
|
|
|
|
|
// Usage model 1: Simulate all C code, doing lifetime analysis
|
2021-11-26 16:52:36 +01:00
|
|
|
LifeVisitor{nodep, m_statep};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-09-26 17:05:35 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeProcedure* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Usage model 2: Cleanup basic blocks
|
2021-11-26 16:52:36 +01:00
|
|
|
LifeVisitor{nodep, m_statep};
|
2006-09-26 19:05:08 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar*) override {} // Accelerate
|
|
|
|
|
void visit(AstNodeStmt*) override {} // Accelerate
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstNodeExpr*) override {} // Accelerate
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-09-26 17:05:35 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
LifeTopVisitor(AstNetlist* nodep, LifeState* statep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_statep{statep} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~LifeTopVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Life class functions
|
|
|
|
|
|
|
|
|
|
void V3Life::lifeAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
|
|
|
|
LifeState state;
|
2021-11-26 16:52:36 +01:00
|
|
|
LifeTopVisitor{nodep, &state};
|
2018-03-10 18:57:50 +01:00
|
|
|
} // Destruct before checking
|
2023-10-09 11:50:31 +02:00
|
|
|
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("life", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|