2020-05-29 00:08:15 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Replace increments/decrements with new variables
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2026-01-27 02:24:34 +01: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-05-29 00:08:15 +02:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3LinkInc's Transformations:
|
|
|
|
|
//
|
2022-12-23 22:17:08 +01:00
|
|
|
// prepost_expr_visit
|
2026-05-02 02:35:51 +02:00
|
|
|
// PREINC/PREDEC
|
2020-05-29 00:08:15 +02:00
|
|
|
// Create a temporary __VIncrementX variable, assign the value of
|
|
|
|
|
// the current variable value to it, substitute the current
|
|
|
|
|
// variable with the temporary one in the statement.
|
|
|
|
|
// Increment/decrement the original variable with by the given
|
|
|
|
|
// value.
|
2026-05-02 02:35:51 +02:00
|
|
|
// POSTINC/POSTDEC
|
2020-05-29 00:08:15 +02:00
|
|
|
// Increment/decrement the current variable by the given value.
|
|
|
|
|
// Create a temporary __VIncrementX variable, assign the value of
|
|
|
|
|
// of the current variable (after the operation) to it. Substitute
|
|
|
|
|
// The original variable with the temporary one in the statement.
|
2025-01-09 01:37:20 +01:00
|
|
|
//
|
2022-12-23 22:17:08 +01:00
|
|
|
// prepost_stmt_visit
|
2026-05-02 02:35:51 +02:00
|
|
|
// PREINC/PREDEC/POSTINC/POSTDEC
|
2020-05-29 00:08:15 +02:00
|
|
|
// Increment/decrement the current variable by the given value.
|
|
|
|
|
// The order (pre/post) doesn't matter outside statements thus
|
|
|
|
|
// the pre/post operations are treated equally and there is no
|
|
|
|
|
// need for a temporary variable.
|
|
|
|
|
//
|
2025-01-09 01:37:20 +01:00
|
|
|
// prepost_stmt_sel_visit
|
|
|
|
|
// For e.g. 'array[something_with_side_eff]++', common in UVM etc
|
|
|
|
|
// PREADD/PRESUB/POSTADD/POSTSUB
|
|
|
|
|
// Create temporary with array index.
|
|
|
|
|
// Increment/decrement using index of the temporary.
|
|
|
|
|
//
|
2020-05-29 00:08:15 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 04:50:27 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3LinkInc.h"
|
|
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
#include "V3LinkLValue.h"
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2020-05-29 00:08:15 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class LinkIncVisitor final : public VNVisitor {
|
2020-05-29 00:08:15 +02:00
|
|
|
// STATE
|
2022-10-19 02:04:09 +02:00
|
|
|
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
2023-08-19 13:03:21 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Module we're inside
|
2026-05-02 02:35:51 +02:00
|
|
|
int m_modCompoundAssignmentsNum = 0; // Var name counter
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
|
|
|
|
bool m_unsupportedHere = false; // Used to detect where it's not supported yet
|
2020-05-29 00:08:15 +02:00
|
|
|
|
2022-03-11 13:34:11 +01:00
|
|
|
// METHODS
|
2023-08-19 13:03:21 +02:00
|
|
|
void insertOnTop(AstNode* newp) {
|
|
|
|
|
// Add the thing directly under the current TFunc/Module
|
|
|
|
|
AstNode* stmtsp = nullptr;
|
|
|
|
|
if (m_ftaskp) {
|
|
|
|
|
stmtsp = m_ftaskp->stmtsp();
|
|
|
|
|
} else if (m_modp) {
|
|
|
|
|
stmtsp = m_modp->stmtsp();
|
|
|
|
|
}
|
2025-09-20 23:40:50 +02:00
|
|
|
UASSERT_OBJ(stmtsp, newp, "Variable not under FTASK/MODULE");
|
2023-08-19 13:03:21 +02:00
|
|
|
newp->addNext(stmtsp->unlinkFrBackWithNext());
|
|
|
|
|
if (m_ftaskp) {
|
|
|
|
|
m_ftaskp->addStmtsp(newp);
|
|
|
|
|
} else if (m_modp) {
|
|
|
|
|
m_modp->addStmtsp(newp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 14:50:39 +02:00
|
|
|
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
2020-05-29 00:08:15 +02:00
|
|
|
// Return node that must be visited, if any
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, newp, "", "newstmt");
|
2025-07-23 14:50:39 +02:00
|
|
|
UASSERT_OBJ(m_insStmtp, nodep, "Expression not underneath a statement");
|
|
|
|
|
m_insStmtp->addHereThisAsNext(newp);
|
2020-05-29 00:08:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2025-09-29 16:25:25 +02:00
|
|
|
if (nodep->dead()) return;
|
2023-08-19 13:03:21 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2026-05-02 02:35:51 +02:00
|
|
|
VL_RESTORER(m_modCompoundAssignmentsNum);
|
2023-08-19 13:03:21 +02:00
|
|
|
m_modp = nodep;
|
2026-05-02 02:35:51 +02:00
|
|
|
m_modCompoundAssignmentsNum = 0;
|
2020-05-29 00:08:15 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-10-19 02:04:09 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
|
|
|
|
VL_RESTORER(m_ftaskp);
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2025-09-15 16:50:31 +02:00
|
|
|
void visit(AstNodeCoverOrAssert* nodep) override {
|
|
|
|
|
VL_RESTORER(m_insStmtp);
|
|
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
iterateAndNextNull(nodep->propp());
|
|
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
// Note: no iterating over sentreep here as they will be ignored anyway
|
|
|
|
|
if (AstAssert* const assertp = VN_CAST(nodep, Assert)) {
|
|
|
|
|
iterateAndNextNull(assertp->failsp());
|
|
|
|
|
} else if (AstAssertIntrinsic* const intrinsicp = VN_CAST(nodep, AssertIntrinsic)) {
|
|
|
|
|
iterateAndNextNull(intrinsicp->failsp());
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep->passsp());
|
|
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstLoop* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
2020-08-15 16:12:55 +02:00
|
|
|
m_insStmtp = nullptr; // First thing should be new statement
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2020-08-15 16:12:55 +02:00
|
|
|
m_insStmtp = nullptr; // Next thing should be new statement
|
2020-05-29 00:08:15 +02:00
|
|
|
}
|
2023-11-12 02:20:37 +01:00
|
|
|
void visit(AstNodeForeach* nodep) override {
|
2021-12-11 21:06:33 +01:00
|
|
|
// Special, as statements need to be put in different places
|
|
|
|
|
// Body insert just before themselves
|
|
|
|
|
m_insStmtp = nullptr; // First thing should be new statement
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Done the loop
|
|
|
|
|
m_insStmtp = nullptr; // Next thing should be new statement
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpBlock* nodep) override {
|
2021-12-11 21:06:33 +01:00
|
|
|
// Special, as statements need to be put in different places
|
|
|
|
|
// Body insert just before themselves
|
|
|
|
|
m_insStmtp = nullptr; // First thing should be new statement
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Done the loop
|
|
|
|
|
m_insStmtp = nullptr; // Next thing should be new statement
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2020-06-03 18:50:24 +02:00
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
2020-08-15 16:12:55 +02:00
|
|
|
m_insStmtp = nullptr;
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->thensp());
|
2020-06-03 18:50:24 +02:00
|
|
|
iterateAndNextNull(nodep->elsesp());
|
2020-08-15 16:12:55 +02:00
|
|
|
m_insStmtp = nullptr;
|
2020-06-03 18:50:24 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCaseItem* nodep) override {
|
2022-03-12 17:24:32 +01:00
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_unsupportedHere);
|
|
|
|
|
m_unsupportedHere = true;
|
|
|
|
|
iterateAndNextNull(nodep->condsp());
|
|
|
|
|
}
|
|
|
|
|
m_insStmtp = nullptr; // Next thing should be new statement
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2022-03-12 17:24:32 +01:00
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstDelay* 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
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstEventControl* 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
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstWait* 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
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
|
|
|
|
m_insStmtp = nullptr;
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
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
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstBegin* nodep) override {
|
|
|
|
|
m_insStmtp = nullptr; // Pretend not a statement TODO: parse ++/-- as ExprStmt
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
m_insStmtp = nullptr; // Next thing should be new statement
|
|
|
|
|
}
|
2025-09-29 21:01:40 +02:00
|
|
|
void visit(AstStmtExpr* nodep) override {
|
|
|
|
|
AstNodeExpr* const exprp = nodep->exprp();
|
2026-05-02 02:35:51 +02:00
|
|
|
if (VN_IS(exprp, PostInc) || VN_IS(exprp, PostDec) || VN_IS(exprp, PreInc)
|
|
|
|
|
|| VN_IS(exprp, PreDec)) {
|
2025-09-29 21:01:40 +02:00
|
|
|
// Repalce this StmtExpr with the expression, visiting it will turn it into a NodeStmt
|
|
|
|
|
nodep->replaceWith(exprp->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
iterate(exprp);
|
|
|
|
|
m_insStmtp = nullptr;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
visit(static_cast<AstNodeStmt*>(nodep));
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeStmt* nodep) override {
|
2020-05-29 00:08:15 +02:00
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_insStmtp = nullptr; // Next thing should be new statement
|
2020-05-29 00:08:15 +02:00
|
|
|
}
|
|
|
|
|
void unsupported_visit(AstNode* nodep) {
|
2022-03-11 13:34:11 +01:00
|
|
|
VL_RESTORER(m_unsupportedHere);
|
2020-05-29 00:08:15 +02:00
|
|
|
m_unsupportedHere = true;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Marking unsupported " << nodep);
|
2020-05-29 00:08:15 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstLogAnd* nodep) override { unsupported_visit(nodep); }
|
|
|
|
|
void visit(AstLogOr* nodep) override { unsupported_visit(nodep); }
|
|
|
|
|
void visit(AstLogEq* nodep) override { unsupported_visit(nodep); }
|
|
|
|
|
void visit(AstLogIf* nodep) override { unsupported_visit(nodep); }
|
2025-08-16 00:49:06 +02:00
|
|
|
void visit(AstCond* nodep) override { unsupported_visit(nodep); }
|
2022-11-01 23:53:47 +01:00
|
|
|
void visit(AstPropSpec* nodep) override { unsupported_visit(nodep); }
|
2026-05-02 02:35:51 +02:00
|
|
|
void prepost_visit(AstNodeUniop* const nodep) {
|
2020-05-29 00:08:15 +02:00
|
|
|
// Check if we are underneath a statement
|
2026-05-02 02:35:51 +02:00
|
|
|
AstSelBit* const selbitp = VN_CAST(nodep->lhsp(), SelBit);
|
2025-01-09 01:37:20 +01:00
|
|
|
if (!m_insStmtp && selbitp && VN_IS(selbitp->fromp(), NodeVarRef)
|
|
|
|
|
&& !selbitp->bitp()->isPure()) {
|
|
|
|
|
prepost_stmt_sel_visit(nodep);
|
2022-12-23 22:17:08 +01:00
|
|
|
} else {
|
2025-01-09 01:37:20 +01:00
|
|
|
if (!m_insStmtp) {
|
|
|
|
|
prepost_stmt_visit(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
prepost_expr_visit(nodep);
|
|
|
|
|
}
|
2020-05-29 00:08:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-02 02:35:51 +02:00
|
|
|
AstNodeExpr* getOperationp(AstNode* const nodep, AstNodeExpr* const lhsp,
|
|
|
|
|
AstNodeExpr* const rhsp) {
|
|
|
|
|
if (VN_IS(nodep, PreDec) || VN_IS(nodep, PostDec)) {
|
|
|
|
|
return new AstSub{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
}
|
|
|
|
|
if (VN_IS(nodep, PreInc) || VN_IS(nodep, PostInc)) {
|
|
|
|
|
return new AstAdd{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
}
|
|
|
|
|
if (AstAssignCompound* assignp = VN_CAST(nodep, AssignCompound)) {
|
|
|
|
|
switch (assignp->operation()) {
|
|
|
|
|
case AstAssignCompound::operation::Add:
|
|
|
|
|
return new AstAdd{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::And:
|
|
|
|
|
return new AstAnd{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::Div:
|
|
|
|
|
return new AstDiv{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::ModDiv:
|
|
|
|
|
return new AstModDiv{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::Mul:
|
|
|
|
|
return new AstMul{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::Or: return new AstOr{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::ShiftL:
|
|
|
|
|
return new AstShiftL{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::ShiftR:
|
|
|
|
|
return new AstShiftR{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::ShiftRS:
|
|
|
|
|
return new AstShiftRS{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::Sub:
|
|
|
|
|
return new AstSub{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
case AstAssignCompound::operation::Xor:
|
|
|
|
|
return new AstXor{nodep->fileline(), lhsp, rhsp};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->v3fatalSrc("Unhandled compound assignment operation");
|
|
|
|
|
}
|
|
|
|
|
void prepost_stmt_sel_visit(AstNodeUniop* const nodep) {
|
2025-01-09 01:37:20 +01:00
|
|
|
// Special case array[something]++, see comments at file top
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, nodep, "", "pp-stmt-sel-in");
|
2025-01-09 01:37:20 +01:00
|
|
|
iterateChildren(nodep);
|
2026-05-02 02:35:51 +02:00
|
|
|
FileLine* fl = nodep->fileline();
|
|
|
|
|
V3Number numOne{fl, 32, 1, false};
|
|
|
|
|
AstNodeExpr* const exprp = new AstConst{nodep->fileline(), numOne};
|
2025-01-09 01:37:20 +01:00
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
prepost_stmt_sel_visit(nodep, nodep->lhsp(), exprp);
|
|
|
|
|
}
|
|
|
|
|
void prepost_stmt_sel_visit(AstAssignCompound* const nodep) {
|
|
|
|
|
// Special case array[something] += expr, see comments at file top
|
|
|
|
|
// UINFOTREE(9, nodep, "", "pp-stmt-sel-in");
|
|
|
|
|
AstNodeExpr* const exprp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
|
|
|
|
|
prepost_stmt_sel_visit(nodep, nodep->lhsp(), exprp);
|
|
|
|
|
}
|
|
|
|
|
void prepost_stmt_sel_visit(AstNode* const nodep, AstNodeExpr* const lhsp,
|
|
|
|
|
AstNodeExpr* const exprp) {
|
|
|
|
|
AstSelBit* const rdSelbitp = VN_AS(lhsp, SelBit);
|
|
|
|
|
AstNodeVarRef* const rdFromp = VN_AS(rdSelbitp->fromp()->cloneTreePure(true), NodeVarRef);
|
|
|
|
|
rdFromp->access(VAccess::READ);
|
2025-01-09 01:37:20 +01:00
|
|
|
AstNodeExpr* const rdBitp = rdSelbitp->bitp()->unlinkFrBack();
|
2026-05-02 02:35:51 +02:00
|
|
|
AstSelBit* const wrSelbitp = VN_CAST(lhsp, SelBit);
|
2025-01-09 01:37:20 +01:00
|
|
|
AstNodeExpr* const wrFromp = wrSelbitp->fromp()->unlinkFrBack();
|
2026-05-02 02:35:51 +02:00
|
|
|
V3LinkLValue::linkLValueSet(wrFromp);
|
2025-01-09 01:37:20 +01:00
|
|
|
|
|
|
|
|
// Prepare a temporary variable
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
2026-05-02 02:35:51 +02:00
|
|
|
const string name = "__VtempIndex"s + cvtToStr(++m_modCompoundAssignmentsNum);
|
2025-01-09 01:37:20 +01:00
|
|
|
AstVar* const varp = new AstVar{
|
|
|
|
|
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
|
|
|
|
|
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, rdBitp->cloneTree(true)}};
|
|
|
|
|
if (m_ftaskp) varp->funcLocal(true);
|
|
|
|
|
|
|
|
|
|
// Declare the variable
|
|
|
|
|
insertOnTop(varp);
|
|
|
|
|
|
|
|
|
|
// Define what operation will we be doing
|
|
|
|
|
AstAssign* const varAssignp
|
|
|
|
|
= new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, rdBitp};
|
|
|
|
|
AstNode* const newp = varAssignp;
|
|
|
|
|
|
|
|
|
|
AstNodeExpr* const valuep
|
|
|
|
|
= new AstSelBit{fl, rdFromp, new AstVarRef{fl, varp, VAccess::READ}};
|
|
|
|
|
AstNodeExpr* const storeTop
|
|
|
|
|
= new AstSelBit{fl, wrFromp, new AstVarRef{fl, varp, VAccess::READ}};
|
|
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
AstAssign* assignp
|
|
|
|
|
= new AstAssign{nodep->fileline(), storeTop, getOperationp(nodep, valuep, exprp)};
|
2025-01-09 01:37:20 +01:00
|
|
|
newp->addNext(assignp);
|
|
|
|
|
|
|
|
|
|
// if (debug() >= 9) newp->dumpTreeAndNext("-pp-stmt-sel-new: ");
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2026-05-02 02:35:51 +02:00
|
|
|
void prepost_stmt_visit(AstNodeUniop* const nodep) {
|
2020-05-29 00:08:15 +02:00
|
|
|
iterateChildren(nodep);
|
2026-05-02 02:35:51 +02:00
|
|
|
AstNodeExpr* const storeTop = nodep->lhsp()->cloneTreePure(true);
|
|
|
|
|
AstNodeExpr* const valuep = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
V3Number numOne{fl, 32, 1, false};
|
|
|
|
|
AstNodeExpr* const exprp = new AstConst{nodep->fileline(), numOne};
|
2020-05-29 00:08:15 +02:00
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
prepost_stmt_visit(nodep, exprp, storeTop, valuep);
|
|
|
|
|
}
|
|
|
|
|
void prepost_stmt_visit(AstAssignCompound* const nodep) {
|
|
|
|
|
AstNodeExpr* const exprp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const storeTop = nodep->lhsp()->cloneTreePure(true);
|
|
|
|
|
AstNodeExpr* const valuep = nodep->lhsp()->unlinkFrBack();
|
2020-05-29 00:08:15 +02:00
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
prepost_stmt_visit(nodep, exprp, storeTop, valuep);
|
|
|
|
|
}
|
|
|
|
|
void prepost_stmt_visit(AstNode* const nodep, AstNodeExpr* const exprp,
|
|
|
|
|
AstNodeExpr* const storeTop, AstNodeExpr* const valuep) {
|
|
|
|
|
V3LinkLValue::linkLValueSet(valuep, false);
|
|
|
|
|
AstAssign* const assignp
|
|
|
|
|
= new AstAssign{nodep->fileline(), storeTop, getOperationp(nodep, valuep, exprp)};
|
2020-05-29 00:08:15 +02:00
|
|
|
nodep->replaceWith(assignp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2026-05-02 02:35:51 +02:00
|
|
|
void prepost_expr_visit(AstNodeUniop* const nodep) {
|
2020-05-29 00:08:15 +02:00
|
|
|
iterateChildren(nodep);
|
2023-04-08 02:57:17 +02:00
|
|
|
if (m_unsupportedHere) {
|
2026-03-26 18:28:43 +01:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Pre/post increment/decrement operator"
|
|
|
|
|
" within a logical expression (&&, ||, ?:, etc.)");
|
2020-05-29 00:08:15 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2026-05-02 02:35:51 +02:00
|
|
|
AstNodeExpr* const readp = nodep->lhsp();
|
|
|
|
|
AstNodeExpr* const writep = nodep->lhsp()->cloneTreePure(true);
|
|
|
|
|
V3LinkLValue::linkLValueSet(readp, false);
|
2020-05-29 00:08:15 +02:00
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
V3Number numOne{fl, 32, 1, false};
|
|
|
|
|
AstNodeExpr* const newconstp = new AstConst{nodep->fileline(), numOne};
|
2020-05-29 00:08:15 +02:00
|
|
|
|
|
|
|
|
// Prepare a temporary variable
|
2026-05-02 02:35:51 +02:00
|
|
|
const string name = "__Vincrement"s + cvtToStr(++m_modCompoundAssignmentsNum);
|
2023-04-08 02:57:17 +02:00
|
|
|
AstVar* const varp = new AstVar{
|
|
|
|
|
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
|
2026-05-02 02:35:51 +02:00
|
|
|
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTreePure(true)}};
|
2026-02-08 23:53:56 +01:00
|
|
|
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2022-10-19 02:04:09 +02:00
|
|
|
if (m_ftaskp) varp->funcLocal(true);
|
2020-05-29 00:08:15 +02:00
|
|
|
|
|
|
|
|
// Declare the variable
|
2023-08-19 13:03:21 +02:00
|
|
|
insertOnTop(varp);
|
2020-05-29 00:08:15 +02:00
|
|
|
|
|
|
|
|
// Define what operation will we be doing
|
2026-05-02 02:35:51 +02:00
|
|
|
AstNodeExpr* const operp = getOperationp(nodep, readp->cloneTreePure(true), newconstp);
|
2020-05-29 00:08:15 +02:00
|
|
|
|
2026-05-02 02:35:51 +02:00
|
|
|
if (VN_IS(nodep, PreInc) || VN_IS(nodep, PreDec)) {
|
|
|
|
|
// PreInc/PreDec operations
|
2020-05-29 00:08:15 +02:00
|
|
|
// Immediately after declaration - increment it by one
|
2023-08-19 13:03:21 +02:00
|
|
|
AstAssign* const assignp
|
|
|
|
|
= new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp};
|
2020-05-29 00:08:15 +02:00
|
|
|
// Immediately after incrementing - assign it to the original variable
|
2025-07-23 14:50:39 +02:00
|
|
|
assignp->addNext(new AstAssign{fl, writep, new AstVarRef{fl, varp, VAccess::READ}});
|
|
|
|
|
insertBeforeStmt(nodep, assignp);
|
2020-05-29 00:08:15 +02:00
|
|
|
} else {
|
2026-05-02 02:35:51 +02:00
|
|
|
// PostInc/PostDec operations
|
2023-08-19 13:03:21 +02:00
|
|
|
// Assign the original variable to the temporary one
|
|
|
|
|
AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
2023-09-17 04:50:54 +02:00
|
|
|
readp->cloneTreePure(true)};
|
2020-05-29 00:08:15 +02:00
|
|
|
// Increment the original variable by one
|
2025-07-23 14:50:39 +02:00
|
|
|
assignp->addNext(new AstAssign{fl, writep, operp});
|
|
|
|
|
insertBeforeStmt(nodep, assignp);
|
2020-05-29 00:08:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace the node with the temporary
|
2023-04-08 02:57:17 +02:00
|
|
|
nodep->replaceWith(new AstVarRef{readp->fileline(), varp, VAccess::READ});
|
2020-05-29 00:08:15 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2026-05-02 02:35:51 +02:00
|
|
|
void visit(AstPreInc* nodep) override { prepost_visit(nodep); }
|
|
|
|
|
void visit(AstPostInc* nodep) override { prepost_visit(nodep); }
|
|
|
|
|
void visit(AstPreDec* nodep) override { prepost_visit(nodep); }
|
|
|
|
|
void visit(AstPostDec* nodep) override { prepost_visit(nodep); }
|
|
|
|
|
void visit(AstAssignCompound* nodep) override {
|
2026-05-07 15:21:04 +02:00
|
|
|
visit(static_cast<AstNodeStmt*>(nodep));
|
2026-05-02 02:35:51 +02:00
|
|
|
AstSelBit* const selbitp = VN_CAST(nodep->lhsp(), SelBit);
|
|
|
|
|
if (!m_insStmtp && selbitp && VN_IS(selbitp->fromp(), NodeVarRef)
|
|
|
|
|
&& !selbitp->bitp()->isPure()) {
|
|
|
|
|
prepost_stmt_sel_visit(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
prepost_stmt_visit(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstGenFor* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-05-29 00:08:15 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit LinkIncVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~LinkIncVisitor() override = default;
|
2020-05-29 00:08:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Task class functions
|
|
|
|
|
|
|
|
|
|
void V3LinkInc::linkIncrements(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ LinkIncVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("linkinc", 0, dumpTreeEitherLevel() >= 3);
|
2020-05-29 00:08:15 +02:00
|
|
|
}
|