2012-04-12 21:08:20 -04:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for premit nodes
|
|
|
|
|
//
|
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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Premit's Transformations:
|
2008-06-09 21:25:10 -04:00
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
// Each module:
|
2019-05-19 16:13:13 -04:00
|
|
|
// For each wide OP, make a a temporary variable with the wide value
|
|
|
|
|
// For each deep expression, assign expression to temporary.
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
2019-09-09 07:50:21 -04:00
|
|
|
// Each display (independent transformation; here as Premit is a good point)
|
2019-05-19 16:13:13 -04:00
|
|
|
// If autoflush, insert a flush
|
2008-07-16 14:06:08 -04:00
|
|
|
//
|
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 "V3Premit.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
|
2021-05-08 20:04:56 +01:00
|
|
|
#include "V3Stats.h"
|
2021-08-11 14:30:00 +01:00
|
|
|
#include "V3UniqueNames.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2022-09-18 20:53:42 +01:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2021-05-08 20:04:56 +01:00
|
|
|
constexpr int STATIC_CONST_MIN_WIDTH = 256; // Minimum size to extract to static constant
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
|
|
|
|
// Premit state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 13:56:40 -05:00
|
|
|
class PremitVisitor final : public VNVisitor {
|
2006-08-26 11:35:28 +00:00
|
|
|
// NODE STATE
|
2022-10-12 10:19:21 +01:00
|
|
|
// AstNodeExpr::user() -> bool. True if iterated already
|
2022-01-09 22:34:10 +00:00
|
|
|
// *::user3() -> Used when visiting AstNodeAssign
|
2022-01-02 13:56:40 -05:00
|
|
|
const VNUser1InUse m_inuser1;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2023-05-27 09:43:23 -04:00
|
|
|
// STATE - across all visitors
|
|
|
|
|
VDouble0 m_extractedToConstPool; // Statistic tracking
|
2025-07-23 10:48:55 +01:00
|
|
|
VDouble0 m_temporaryVarsCreated; // Statistic tracking
|
2023-05-27 09:43:23 -04:00
|
|
|
|
2023-05-21 14:06:39 -04:00
|
|
|
// STATE - for current visit position (use VL_RESTORER)
|
2020-10-31 08:59:35 -04:00
|
|
|
AstCFunc* m_cfuncp = nullptr; // Current block
|
2023-12-06 15:42:46 +01:00
|
|
|
size_t m_tmpVarCnt = 0; // Number of temporary variables created inside a function
|
2020-08-16 09:55:36 -04:00
|
|
|
AstNode* m_stmtp = nullptr; // Current statement
|
|
|
|
|
bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
// METHODS
|
2026-06-01 20:25:41 +01:00
|
|
|
bool needsTemp(AstNodeExpr* nodep) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Consider adding a temp for this expression.
|
2026-06-01 20:25:41 +01:00
|
|
|
if (!m_stmtp) return false; // Not under a statement
|
|
|
|
|
if (nodep->user1SetOnce()) return false; // Already processed
|
|
|
|
|
if (!nodep->isWide()) return false; // Not wide
|
|
|
|
|
if (m_assignLhs) return false; // This is an lvalue!
|
2023-12-06 15:42:46 +01:00
|
|
|
UASSERT_OBJ(!VN_IS(nodep->firstAbovep(), ArraySel), nodep, "Should have been ignored");
|
2026-06-01 20:25:41 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkNode(AstNodeExpr* nodep) {
|
|
|
|
|
if (needsTemp(nodep)) createTemp(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstVarRef* isRhsOfAssignToVar(AstNodeExpr* nodep) {
|
|
|
|
|
// If enclosing statement is an assign
|
|
|
|
|
AstNodeAssign* const assignp = VN_CAST(m_stmtp, NodeAssign);
|
|
|
|
|
if (!assignp) return nullptr;
|
|
|
|
|
// of which 'nodep' is the RHS of
|
|
|
|
|
if (assignp->rhsp() != nodep) return nullptr;
|
|
|
|
|
// and the LHS is a sipmple variable reference
|
|
|
|
|
AstVarRef* const lRefp = VN_CAST(assignp->lhsp(), VarRef);
|
|
|
|
|
if (!lRefp) return nullptr;
|
|
|
|
|
AstVar* const varp = lRefp->varp();
|
|
|
|
|
// And the RHS does not use the same variable
|
|
|
|
|
if (nodep->exists([varp](const AstVarRef* refp) { return refp->varp() == varp; })) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// Then return the LHS reference
|
|
|
|
|
return lRefp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new temporary that can hold the value of the given expression
|
|
|
|
|
AstVar* newTmpFor(AstNodeExpr* nodep) {
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
const std::string name = "__Vtemp_" + std::to_string(++m_tmpVarCnt);
|
|
|
|
|
AstVar* const varp = new AstVar{flp, VVarType::STMTTEMP, name, nodep->dtypep()};
|
|
|
|
|
varp->funcLocal(true);
|
|
|
|
|
varp->noReset(true);
|
|
|
|
|
m_cfuncp->addVarsp(varp);
|
|
|
|
|
++m_temporaryVarsCreated;
|
|
|
|
|
return varp;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-23 10:48:55 +01:00
|
|
|
AstVar* createTemp(AstNodeExpr* nodep) {
|
2023-12-06 15:42:46 +01:00
|
|
|
UASSERT_OBJ(m_stmtp, nodep, "Attempting to create temporary with no insertion point");
|
2025-07-23 10:48:55 +01:00
|
|
|
UINFO(4, "createTemp: " << nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2022-01-02 10:32:35 -05:00
|
|
|
VNRelinker relinker;
|
2021-06-13 15:05:55 +01:00
|
|
|
nodep->unlinkFrBack(&relinker);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2023-12-06 15:42:46 +01:00
|
|
|
FileLine* const flp = nodep->fileline();
|
2021-05-08 20:04:56 +01:00
|
|
|
AstConst* const constp = VN_CAST(nodep, Const);
|
2021-06-13 15:05:55 +01:00
|
|
|
const bool useConstPool = constp // Is a constant
|
|
|
|
|
&& (constp->width() >= STATIC_CONST_MIN_WIDTH) // Large enough
|
|
|
|
|
&& !constp->num().isFourState() // Not four state
|
|
|
|
|
&& !constp->num().isString(); // Not a string
|
2023-12-06 15:42:46 +01:00
|
|
|
|
|
|
|
|
AstVar* varp = nullptr;
|
|
|
|
|
|
2021-06-13 15:05:55 +01:00
|
|
|
if (useConstPool) {
|
|
|
|
|
// Extract into constant pool.
|
2022-06-03 19:41:59 -04:00
|
|
|
const bool merge = v3Global.opt.fMergeConstPool();
|
2021-06-13 15:05:55 +01:00
|
|
|
varp = v3Global.rootp()->constPoolp()->findConst(constp, merge)->varp();
|
2024-05-08 08:36:24 -04:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2021-06-13 15:05:55 +01:00
|
|
|
++m_extractedToConstPool;
|
2021-05-08 20:04:56 +01:00
|
|
|
} else {
|
2023-12-06 15:42:46 +01:00
|
|
|
// Keep as local temporary.
|
2026-06-01 20:25:41 +01:00
|
|
|
varp = newTmpFor(nodep);
|
2025-07-23 13:50:39 +01:00
|
|
|
// Assignment to put before the referencing statement
|
2026-06-01 20:25:41 +01:00
|
|
|
AstVarRef* const refp = new AstVarRef{flp, varp, VAccess::WRITE};
|
|
|
|
|
AstAssign* const assignp = new AstAssign{flp, refp, nodep};
|
2025-07-23 13:50:39 +01:00
|
|
|
// Insert before the statement
|
|
|
|
|
m_stmtp->addHereThisAsNext(assignp);
|
2021-05-08 20:04:56 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:05:55 +01:00
|
|
|
// Replace node with VarRef to new Var
|
2023-12-06 15:42:46 +01:00
|
|
|
relinker.relink(new AstVarRef{flp, varp, VAccess::READ});
|
2023-05-21 12:03:13 -04:00
|
|
|
|
2023-12-06 15:42:46 +01:00
|
|
|
// Return the temporary variable
|
|
|
|
|
return varp;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2023-05-21 12:03:13 -04:00
|
|
|
|
2018-08-25 09:52:45 -04:00
|
|
|
void visitShift(AstNodeBiop* nodep) {
|
2025-05-22 20:29:32 -04:00
|
|
|
UINFO(4, " ShiftFix " << nodep);
|
2026-06-22 10:06:26 +01:00
|
|
|
UASSERT_OBJ(VN_IS(nodep, ShiftL) || VN_IS(nodep, ShiftR) || VN_IS(nodep, ShiftRS), nodep,
|
|
|
|
|
"Bad case");
|
|
|
|
|
// Shift larger than the width of the type (overshift) is undefined behavour in C++
|
|
|
|
|
// (in practice will shift by the wrapped shift amount). These are requierd to go to
|
|
|
|
|
// zero/msbs, so replacing them here.
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
if (const AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const)) {
|
|
|
|
|
// Shift amount known to be constant. If oversized shift, replace with zero/msbs.
|
|
|
|
|
// Otherwise we can leave the original shifts which have better constant folding
|
|
|
|
|
// than the *Ovr versions.
|
|
|
|
|
const bool isOversized = shiftp->num().mostSetBitP1() > 32 //
|
|
|
|
|
|| (shiftp->num().toSQuad() >= nodep->width());
|
|
|
|
|
if (isOversized) {
|
|
|
|
|
AstNodeExpr* newp = nullptr;
|
|
|
|
|
if (VN_IS(nodep, ShiftRS)) {
|
|
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const msbp = new AstSel{flp, lhsp, nodep->width() - 1, 1};
|
|
|
|
|
newp = new AstExtendS{flp, msbp, nodep->width()};
|
|
|
|
|
} else {
|
|
|
|
|
newp = new AstConst{flp, AstConst::DTyped{}, nodep->dtypep()};
|
|
|
|
|
}
|
|
|
|
|
nodep->replaceWithKeepDType(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Shift amount not known at compile time. Convert to *Ovr version. Don't need to do
|
|
|
|
|
// if it would use a wide operation which works correctly at runtime, of if the max
|
|
|
|
|
// value of the shift amount is less than the with of the shifted value.
|
|
|
|
|
if (nodep->widthMin() <= VL_QUADSIZE
|
|
|
|
|
&& (nodep->width() < (1LL << nodep->rhsp()->widthMin()))) {
|
|
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* newp = nullptr;
|
|
|
|
|
if (VN_IS(nodep, ShiftL)) {
|
|
|
|
|
newp = new AstShiftLOvr{flp, lhsp, rhsp};
|
|
|
|
|
} else if (VN_IS(nodep, ShiftR)) {
|
|
|
|
|
newp = new AstShiftROvr{flp, lhsp, rhsp};
|
|
|
|
|
} else {
|
|
|
|
|
newp = new AstShiftRSOvr{flp, lhsp, rhsp};
|
|
|
|
|
}
|
|
|
|
|
nodep->replaceWithKeepDType(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
return;
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 07:58:34 -04:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2023-12-06 15:42:46 +01:00
|
|
|
|
2025-08-21 09:43:37 +01:00
|
|
|
static bool rhsReadsLhs(const AstNodeAssign* nodep) {
|
2023-12-06 15:42:46 +01:00
|
|
|
const VNUser3InUse user3InUse;
|
|
|
|
|
nodep->lhsp()->foreach([](const AstVarRef* refp) {
|
|
|
|
|
if (refp->access().isWriteOrRW()) refp->varp()->user3(true);
|
|
|
|
|
});
|
|
|
|
|
return nodep->rhsp()->exists([](const AstVarRef* refp) {
|
|
|
|
|
return refp->access().isReadOnly() && refp->varp()->user3();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
void visit(AstCFunc* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!m_cfuncp, nodep, "Should not nest");
|
|
|
|
|
VL_RESTORER(m_cfuncp);
|
|
|
|
|
VL_RESTORER(m_tmpVarCnt);
|
|
|
|
|
m_cfuncp = nodep;
|
|
|
|
|
m_tmpVarCnt = 0;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS - Statements
|
|
|
|
|
#define START_STATEMENT_OR_RETURN(stmtp) \
|
|
|
|
|
if (!m_cfuncp) return; \
|
2026-03-27 23:14:18 -04:00
|
|
|
if ((stmtp)->user1SetOnce()) return; \
|
2023-12-06 15:42:46 +01:00
|
|
|
VL_RESTORER(m_assignLhs); \
|
|
|
|
|
VL_RESTORER(m_stmtp); \
|
|
|
|
|
m_assignLhs = false; \
|
2026-03-27 23:14:18 -04:00
|
|
|
m_stmtp = (stmtp);
|
2025-09-29 16:25:25 +02:00
|
|
|
|
2023-12-06 15:42:46 +01:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
|
|
|
|
START_STATEMENT_OR_RETURN(nodep);
|
|
|
|
|
|
2025-03-13 13:56:29 +01:00
|
|
|
if (AstCvtArrayToPacked* const packedp = VN_CAST(nodep->lhsp(), CvtArrayToPacked)) {
|
|
|
|
|
// AstCvtArrayToPacked is converted to VL_PACK, which returns rvalue,
|
|
|
|
|
// so it shouldn't be on the LHS. It is now replaced with unpacking of RHS.
|
|
|
|
|
AstNodeExpr* const exprLhsp = packedp->fromp()->unlinkFrBack();
|
|
|
|
|
packedp->replaceWith(exprLhsp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(packedp), packedp);
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
nodep->rhsp(new AstCvtPackedToArray{rhsp->fileline(), rhsp, exprLhsp->dtypep()});
|
|
|
|
|
nodep->dtypeFrom(exprLhsp);
|
|
|
|
|
}
|
2023-12-06 15:42:46 +01:00
|
|
|
// Direct assignment to a simple variable
|
|
|
|
|
if (VN_IS(nodep->lhsp(), VarRef) && !AstVar::scVarRecurse(nodep->lhsp())) {
|
|
|
|
|
AstNode* const rhsp = nodep->rhsp();
|
2026-06-01 20:25:41 +01:00
|
|
|
// Rhs is already a var ref, so nothing to do
|
2023-12-06 15:42:46 +01:00
|
|
|
if (VN_IS(rhsp, VarRef) && !AstVar::scVarRecurse(rhsp)) return;
|
2026-06-01 20:25:41 +01:00
|
|
|
if (VN_IS(rhsp, Cond)) {
|
|
|
|
|
// Do replace Cond on RHS, even if a simple assignment
|
|
|
|
|
} else if (!VN_IS(rhsp, Const)) {
|
2023-12-06 15:42:46 +01:00
|
|
|
// Don't replace the rhs, it's already a simple assignment
|
|
|
|
|
rhsp->user1(true);
|
|
|
|
|
} else if (rhsp->width() < STATIC_CONST_MIN_WIDTH) {
|
|
|
|
|
// It's a small constant, so nothing to do, otherwise will be put to const pool
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 10:48:55 +01:00
|
|
|
// If the RHS reads the LHS, we need a temporary unless the update is atomic
|
|
|
|
|
const bool isAtomic = VN_IS(nodep->lhsp(), VarRef) && !nodep->lhsp()->isWide();
|
|
|
|
|
if (!isAtomic && rhsReadsLhs(nodep)) {
|
|
|
|
|
createTemp(nodep->rhsp());
|
2023-12-06 15:42:46 +01:00
|
|
|
} else {
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_assignLhs = true; // Restored by VL_RESTORER in START_STATEMENT_OR_RETURN
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2026-06-01 20:25:41 +01:00
|
|
|
|
|
|
|
|
// It's possible we end up with an 'a' = 'a' after expanding an AstCond
|
|
|
|
|
if (AstVarRef* const rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
|
|
|
|
|
if (AstVarRef* const lhsp = VN_CAST(nodep->lhsp(), VarRef)) {
|
|
|
|
|
if (lhsp->varp() == rhsp->varp()) {
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-06 15:42:46 +01:00
|
|
|
}
|
|
|
|
|
void visit(AstDisplay* nodep) override {
|
|
|
|
|
START_STATEMENT_OR_RETURN(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (v3Global.opt.autoflush()) {
|
|
|
|
|
const AstNode* searchp = nodep->nextp();
|
|
|
|
|
while (searchp && VN_IS(searchp, Comment)) searchp = searchp->nextp();
|
|
|
|
|
if (searchp && VN_IS(searchp, Display)
|
|
|
|
|
&& nodep->filep()->sameGateTree(VN_AS(searchp, Display)->filep())) {
|
|
|
|
|
// There's another display next; we can just wait to flush
|
|
|
|
|
} else {
|
2025-05-22 20:29:32 -04:00
|
|
|
UINFO(4, "Autoflush " << nodep);
|
2023-12-06 15:42:46 +01:00
|
|
|
nodep->addNextHere(
|
|
|
|
|
new AstFFlush{nodep->fileline(),
|
|
|
|
|
nodep->filep() ? nodep->filep()->cloneTreePure(true) : nullptr});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeStmt* nodep) override {
|
|
|
|
|
START_STATEMENT_OR_RETURN(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef START_STATEMENT_OR_RETURN
|
|
|
|
|
|
|
|
|
|
// VISITORS - Expressions
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstShiftL* nodep) override { visitShift(nodep); }
|
|
|
|
|
void visit(AstShiftR* nodep) override { visitShift(nodep); }
|
|
|
|
|
void visit(AstShiftRS* nodep) override { visitShift(nodep); }
|
2023-12-06 15:42:46 +01:00
|
|
|
|
|
|
|
|
void visit(AstConst* nodep) override { checkNode(nodep); }
|
2006-08-26 11:35:28 +00:00
|
|
|
// Operators
|
2023-12-06 15:42:46 +01:00
|
|
|
void visit(AstNodeTermop* nodep) override { checkNode(nodep); }
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeUniop* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
2017-09-19 20:04:45 -04:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeBiop* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
2017-09-19 20:04:45 -04:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstRand* nodep) override {
|
2021-08-23 20:13:09 -04:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
|
|
|
|
}
|
2023-04-01 10:50:27 -04:00
|
|
|
void visit(AstRandRNG* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
|
|
|
|
}
|
2025-10-21 13:41:29 +02:00
|
|
|
void visit(AstCExprUser* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
iterateChildren(nodep);
|
2024-08-06 14:48:46 +02:00
|
|
|
checkNode(nodep);
|
|
|
|
|
}
|
2026-04-21 17:54:42 +02:00
|
|
|
void visit(AstCMethodHard* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
|
|
|
|
}
|
2024-08-06 14:48:46 +02:00
|
|
|
void visit(AstCvtArrayToPacked* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
2025-04-14 18:40:17 -07:00
|
|
|
checkNode(nodep);
|
|
|
|
|
}
|
2025-04-15 17:50:43 +02:00
|
|
|
void visit(AstCvtPackedToArray* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkNode(nodep);
|
2025-07-23 10:48:55 +01:00
|
|
|
if (!VN_IS(nodep->backp(), NodeAssign)) createTemp(nodep);
|
2025-04-15 17:50:43 +02:00
|
|
|
}
|
2025-04-14 18:40:17 -07:00
|
|
|
void visit(AstCvtUnpackedToQueue* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
2020-04-15 07:58:34 -04:00
|
|
|
checkNode(nodep);
|
2017-09-19 20:04:45 -04:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstSel* nodep) override {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
2020-04-15 07:58:34 -04:00
|
|
|
{ // Only the 'from' is part of the assignment LHS
|
2020-08-24 21:10:43 -04:00
|
|
|
VL_RESTORER(m_assignLhs);
|
2019-05-19 16:13:13 -04:00
|
|
|
m_assignLhs = false;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->lsbp());
|
2023-12-06 15:42:46 +01:00
|
|
|
// AstSel::widthp() must remain a constant, so not iterating
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
checkNode(nodep);
|
2017-09-19 20:04:45 -04:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstArraySel* nodep) override {
|
2025-04-15 17:50:43 +02:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
2020-04-15 07:58:34 -04:00
|
|
|
{ // Only the 'from' is part of the assignment LHS
|
2020-08-24 21:10:43 -04:00
|
|
|
VL_RESTORER(m_assignLhs);
|
2019-12-01 11:52:48 -05:00
|
|
|
m_assignLhs = false;
|
2025-04-15 17:50:43 +02:00
|
|
|
iterateAndNextNull(nodep->bitp());
|
2019-12-01 11:52:48 -05:00
|
|
|
}
|
2023-12-06 15:42:46 +01:00
|
|
|
// ArraySel are just pointer arithmetic and should never be replaced
|
2019-12-01 11:52:48 -05:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstAssocSel* nodep) override {
|
2019-12-01 11:52:48 -05:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
2020-04-15 07:58:34 -04:00
|
|
|
{ // Only the 'from' is part of the assignment LHS
|
2020-08-24 21:10:43 -04:00
|
|
|
VL_RESTORER(m_assignLhs);
|
2019-05-19 16:13:13 -04:00
|
|
|
m_assignLhs = false;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->bitp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
checkNode(nodep);
|
2017-09-19 20:04:45 -04:00
|
|
|
}
|
2026-06-19 19:46:13 +01:00
|
|
|
void visit(AstMatchMasked* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!nodep->user1SetOnce()) {
|
|
|
|
|
// Don't want this replicated by V3Expand
|
|
|
|
|
AstVar* const varp = createTemp(nodep);
|
|
|
|
|
varp->noSubst(true); // Do not re-inline in V3Subst
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-15 23:49:06 +01:00
|
|
|
void visit(AstCond* nodep) override {
|
2026-06-01 20:25:41 +01:00
|
|
|
// Convert AstCond to AstIf in order to avoid evaluating
|
|
|
|
|
// sub-expressions in both branches unconditionally.
|
|
|
|
|
if (needsTemp(nodep)) {
|
|
|
|
|
// Check if LHS variable could be used directly
|
|
|
|
|
AstVarRef* const lRefp = isRhsOfAssignToVar(nodep);
|
|
|
|
|
// If not, create a new temporary variable
|
|
|
|
|
AstVar* varp = lRefp ? lRefp->varp() : newTmpFor(nodep);
|
|
|
|
|
// Can't substitute across basic blocks
|
|
|
|
|
varp->noSubst(true);
|
|
|
|
|
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
// Create 'then' assignment
|
|
|
|
|
AstVarRef* const thenRefp = new AstVarRef{flp, varp, VAccess::WRITE};
|
|
|
|
|
if (lRefp) thenRefp->selfPointer(lRefp->selfPointer());
|
|
|
|
|
AstNodeExpr* const thenExprp = nodep->thenp()->unlinkFrBack();
|
|
|
|
|
AstAssign* const thenAsspp = new AstAssign{flp, thenRefp, thenExprp};
|
|
|
|
|
// Create 'else' assignment
|
|
|
|
|
AstVarRef* const elseRefp = new AstVarRef{flp, varp, VAccess::WRITE};
|
|
|
|
|
if (lRefp) elseRefp->selfPointer(lRefp->selfPointer());
|
|
|
|
|
AstNodeExpr* const elseExprp = nodep->elsep()->unlinkFrBack();
|
|
|
|
|
AstAssign* const elseAsspp = new AstAssign{flp, elseRefp, elseExprp};
|
|
|
|
|
// Creae 'if' and insert it before the statement
|
|
|
|
|
AstNodeExpr* const condp = nodep->condp()->unlinkFrBack();
|
|
|
|
|
AstIf* const ifp = new AstIf{flp, condp, thenAsspp, elseAsspp};
|
|
|
|
|
m_stmtp->addHereThisAsNext(ifp);
|
|
|
|
|
// Replace the AstCond with the result variable
|
|
|
|
|
nodep->replaceWith(new AstVarRef{flp, varp, VAccess::READ});
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
// Splitting to multiple statements can change purity
|
2023-10-15 06:25:42 -04:00
|
|
|
VIsCached::clearCacheTree();
|
2026-06-01 20:25:41 +01:00
|
|
|
// Iterate the resulting assignments
|
|
|
|
|
iterate(ifp);
|
|
|
|
|
return;
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2026-06-01 20:25:41 +01:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
// Any strings sent to a display must be var of string data type,
|
|
|
|
|
// to avoid passing a pointer to a temporary.
|
2026-03-14 22:43:56 -04:00
|
|
|
AstNodeExpr* exprsp = nodep->exprsp();
|
|
|
|
|
if (nodep->exprFormat()) exprsp = VN_AS(exprsp->nextp(), NodeExpr);
|
|
|
|
|
for (AstNodeExpr *argp = exprsp, *nextp; argp; argp = nextp) {
|
|
|
|
|
nextp = VN_AS(argp->nextp(), NodeExpr);
|
|
|
|
|
|
|
|
|
|
AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg);
|
|
|
|
|
AstNodeExpr* const subargp = fargp ? fargp->exprp() : argp;
|
|
|
|
|
// Must avoid taking address of rvalue, so even Const needs a temp
|
|
|
|
|
if (subargp->isString() && !VN_IS(subargp, VarRef)) {
|
|
|
|
|
AstVar* const varp = createTemp(subargp);
|
2023-12-06 15:42:46 +01:00
|
|
|
// Do not remove VarRefs to this in V3Const
|
|
|
|
|
varp->noSubst(true);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2014-11-28 15:01:50 -05:00
|
|
|
}
|
2008-07-16 14:06:08 -04:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstVar*) override {} // Don't hit varrefs under vars
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 07:22:22 -04:00
|
|
|
// CONSTRUCTORS
|
2023-08-28 15:44:41 +02:00
|
|
|
explicit PremitVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-07-30 10:01:25 -04:00
|
|
|
~PremitVisitor() override {
|
2021-06-13 15:05:55 +01:00
|
|
|
V3Stats::addStat("Optimizations, Prelim extracted value to ConstPool",
|
|
|
|
|
m_extractedToConstPool);
|
2025-07-23 10:48:55 +01:00
|
|
|
V3Stats::addStat("Optimizations, Prelim temporary variables created",
|
|
|
|
|
m_temporaryVarsCreated);
|
2021-05-08 20:04:56 +01:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Premit class functions
|
|
|
|
|
|
|
|
|
|
void V3Premit::premitAll(AstNetlist* nodep) {
|
2025-05-22 20:29:32 -04:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 10:52:36 -05:00
|
|
|
{ PremitVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("premit", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|