2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2009-07-14 17:24:21 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Simulate code to determine output values/variables
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2009-07-14 17:24:21 +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-07-14 17:24:21 +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
|
2009-07-14 17:24:21 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// void example_usage() {
|
2022-07-09 13:28:28 +02:00
|
|
|
// SimulateVisitor simvis{false, false};
|
2019-05-19 22:13:13 +02:00
|
|
|
// simvis.clear();
|
|
|
|
|
// // Set all inputs to the constant
|
|
|
|
|
// for (deque<AstVarScope*>::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) {
|
2019-07-23 19:58:17 +02:00
|
|
|
// simvis.newValue(invscp, #);
|
2019-05-19 22:13:13 +02:00
|
|
|
// }
|
|
|
|
|
// // Simulate
|
|
|
|
|
// simvis.main(nodep);
|
|
|
|
|
// // Read outputs
|
|
|
|
|
// for (deque<AstVarScope*>::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) {
|
2019-07-23 19:58:17 +02:00
|
|
|
// AstConst* outconstp = simvis.fetchOutNumberNull(outvscp);
|
2009-07-14 17:24:21 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2021-03-04 03:57:07 +01:00
|
|
|
#ifndef VERILATOR_V3SIMULATE_H_
|
|
|
|
|
#define VERILATOR_V3SIMULATE_H_
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
#include "V3Ast.h"
|
2023-10-29 02:12:27 +02:00
|
|
|
#include "V3AstUserAllocator.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Error.h"
|
2009-12-01 00:36:31 +01:00
|
|
|
#include "V3Task.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Width.h"
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2009-10-31 15:08:38 +01:00
|
|
|
#include <deque>
|
2017-05-10 00:54:15 +02:00
|
|
|
#include <sstream>
|
2022-01-08 18:01:39 +01:00
|
|
|
#include <stack>
|
2022-01-09 22:49:38 +01:00
|
|
|
#include <string>
|
2022-01-08 18:01:39 +01:00
|
|
|
#include <unordered_map>
|
2021-07-05 16:37:20 +02:00
|
|
|
#include <vector>
|
2009-10-31 15:08:38 +01:00
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Simulate class functions
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class SimStackNode final {
|
2017-05-10 00:54:15 +02:00
|
|
|
public:
|
|
|
|
|
// MEMBERS
|
2022-01-01 17:46:49 +01:00
|
|
|
AstFuncRef* const m_funcp;
|
|
|
|
|
V3TaskConnects* const m_tconnects;
|
2017-05-10 00:54:15 +02:00
|
|
|
// CONSTRUCTORS
|
2019-11-09 17:30:31 +01:00
|
|
|
SimStackNode(AstFuncRef* funcp, V3TaskConnects* tconnects)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_funcp{funcp}
|
|
|
|
|
, m_tconnects{tconnects} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
~SimStackNode() = default;
|
2017-05-10 00:54:15 +02:00
|
|
|
};
|
|
|
|
|
|
2023-03-18 17:17:25 +01:00
|
|
|
class SimulateVisitor VL_NOT_FINAL : public VNVisitorConst {
|
2009-07-14 17:24:21 +02:00
|
|
|
// Simulate a node tree, returning value of variables
|
|
|
|
|
// Two major operating modes:
|
|
|
|
|
// Test the tree to see if it is conformant
|
|
|
|
|
// Given a set of input values, find the output values
|
|
|
|
|
// Both are done in this same visitor to reduce risk; if a visitor
|
2022-03-31 02:17:59 +02:00
|
|
|
// is missing, we will not apply the optimization, rather then bomb.
|
2009-07-14 17:24:21 +02:00
|
|
|
|
|
|
|
|
private:
|
2025-08-30 13:45:35 +02:00
|
|
|
// CONSTANTS
|
|
|
|
|
static constexpr int CONST_FUNC_RECURSION_MAX = 1000;
|
|
|
|
|
static constexpr int CALL_STACK_MAX = 100;
|
|
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared on each always/assignw
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2025-01-03 11:33:29 +01:00
|
|
|
// AstNode::user1p() -> See AuxAstVar via m_varAux
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2020-08-16 18:05:35 +02:00
|
|
|
enum VarUsage : uint8_t { VU_NONE = 0, VU_LV = 1, VU_RV = 2, VU_LVDLY = 4 };
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2023-10-29 02:12:27 +02:00
|
|
|
struct AuxVariable final {
|
|
|
|
|
// Checking:
|
|
|
|
|
// Set true to indicate tracking as lvalue/rvalue
|
|
|
|
|
uint8_t usage = VU_NONE;
|
|
|
|
|
// Simulating:
|
|
|
|
|
// Output value of variable (delayed assignments)
|
|
|
|
|
AstNodeExpr* outValuep = nullptr;
|
|
|
|
|
// Input value of variable or node (and output for non-delayed assignments)
|
|
|
|
|
AstNodeExpr* valuep = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AstUser1Allocator<AstNode, AuxVariable> m_varAux;
|
|
|
|
|
|
2025-01-03 11:33:29 +01:00
|
|
|
// We want to re-use allocated constants across calls to clear(), but we want to be able
|
|
|
|
|
// to 'clear()' fast, so we use a generation number based allocator.
|
|
|
|
|
struct ConstAllocator final {
|
|
|
|
|
size_t m_generation = 0;
|
|
|
|
|
size_t m_nextFree = 0;
|
|
|
|
|
std::deque<AstConst*> m_constps;
|
|
|
|
|
AstConst* allocate(size_t currentGeneration, AstNode* nodep) {
|
|
|
|
|
if (m_generation != currentGeneration) {
|
|
|
|
|
m_generation = currentGeneration;
|
|
|
|
|
m_nextFree = 0;
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(m_nextFree <= m_constps.size(), nodep, "Should only allocate at end");
|
|
|
|
|
if (m_nextFree == m_constps.size()) {
|
|
|
|
|
m_constps.push_back(
|
|
|
|
|
new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()});
|
|
|
|
|
}
|
|
|
|
|
AstConst* const constp = m_constps[m_nextFree++];
|
|
|
|
|
constp->num().nodep(nodep);
|
|
|
|
|
return constp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ConstAllocator() {
|
|
|
|
|
for (AstConst* const constp : m_constps) VL_DO_DANGLING(delete constp, constp);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
// STATE
|
2009-07-17 20:13:11 +02:00
|
|
|
// Major mode
|
2020-04-15 13:58:34 +02:00
|
|
|
bool m_checkOnly; ///< Checking only (no simulation) mode
|
|
|
|
|
bool m_scoped; ///< Running with AstVarScopes instead of AstVars
|
|
|
|
|
bool m_params; ///< Doing parameter propagation
|
2009-07-14 17:24:21 +02:00
|
|
|
// Checking:
|
2020-08-15 16:12:55 +02:00
|
|
|
string m_whyNotOptimizable; ///< String explaining why not optimizable or nullptr to optimize
|
2020-04-15 13:58:34 +02:00
|
|
|
AstNode* m_whyNotNodep; ///< First node not optimizable
|
|
|
|
|
bool m_anyAssignDly; ///< True if found a delayed assignment
|
|
|
|
|
bool m_anyAssignComb; ///< True if found a non-delayed assignment
|
|
|
|
|
bool m_inDlyAssign; ///< Under delayed assignment
|
2024-07-02 15:22:32 +02:00
|
|
|
bool m_isImpure; // Not pure
|
2024-10-11 03:22:06 +02:00
|
|
|
bool m_isCoverage; // Has coverage
|
2020-04-15 13:58:34 +02:00
|
|
|
int m_instrCount; ///< Number of nodes
|
|
|
|
|
int m_dataCount; ///< Bytes of data
|
2025-08-30 13:45:35 +02:00
|
|
|
int m_recurseCount = 0; // Now deep in current recursion
|
2025-09-29 16:25:25 +02:00
|
|
|
AstNode* m_jumptargetp = nullptr; // AstJumpBlock/AstLoop we are branching over
|
2009-07-14 17:24:21 +02:00
|
|
|
// Simulating:
|
2025-01-03 11:33:29 +01:00
|
|
|
// Allocators for constants of various data types
|
|
|
|
|
std::unordered_map<const AstNodeDType*, ConstAllocator> m_constps;
|
|
|
|
|
size_t m_constGeneration = 0;
|
2025-08-30 13:45:35 +02:00
|
|
|
std::vector<SimStackNode*> m_callStack; // Call stack for verbose error messages
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2017-03-17 23:40:16 +01:00
|
|
|
// Cleanup
|
2019-05-19 22:13:13 +02:00
|
|
|
// V3Numbers that represents strings are a bit special and the API for
|
|
|
|
|
// V3Number does not allow changing them.
|
2021-07-05 16:37:20 +02:00
|
|
|
std::vector<AstNode*> m_reclaimValuesp; // List of allocated string numbers
|
2017-03-17 23:40:16 +01:00
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
// Note level 8&9 include debugging each simulation value
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2017-05-10 00:54:15 +02:00
|
|
|
// Potentially very slow, intended for debugging
|
2025-08-19 23:02:10 +02:00
|
|
|
string prettyNumber(const V3Number* nump, const AstNodeDType* dtypep) {
|
|
|
|
|
if (const AstRefDType* const refdtypep = VN_CAST(dtypep, RefDType)) { //
|
2019-05-19 22:13:13 +02:00
|
|
|
dtypep = refdtypep->skipRefp();
|
|
|
|
|
}
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstNodeUOrStructDType* const stp = VN_CAST(dtypep, NodeUOrStructDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (stp->packed()) {
|
2018-02-02 03:24:41 +01:00
|
|
|
std::ostringstream out;
|
2020-04-15 13:58:34 +02:00
|
|
|
out << "'{";
|
|
|
|
|
for (AstMemberDType* itemp = stp->membersp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int width = itemp->width();
|
|
|
|
|
const int lsb = itemp->lsb();
|
|
|
|
|
const int msb = lsb + width - 1;
|
2022-07-09 13:28:28 +02:00
|
|
|
V3Number fieldNum{nump, width};
|
2019-05-10 02:03:19 +02:00
|
|
|
fieldNum.opSel(*nump, msb, lsb);
|
2020-04-15 13:58:34 +02:00
|
|
|
out << itemp->name() << ": ";
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const AstNodeDType* const childTypep = itemp->subDTypep()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
out << prettyNumber(&fieldNum, childTypep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
out << fieldNum;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (itemp->nextp()) out << ", ";
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
out << "}";
|
2019-05-19 22:13:13 +02:00
|
|
|
return out.str();
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstPackArrayDType* const arrayp = VN_CAST(dtypep, PackArrayDType)) {
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const AstNodeDType* const childTypep = arrayp->subDTypep()) {
|
2018-02-02 03:24:41 +01:00
|
|
|
std::ostringstream out;
|
2020-04-15 13:58:34 +02:00
|
|
|
out << "[";
|
2021-06-21 00:32:57 +02:00
|
|
|
const int arrayElements = arrayp->elementsConst();
|
2019-05-19 22:13:13 +02:00
|
|
|
for (int element = 0; element < arrayElements; ++element) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int width = childTypep->width();
|
|
|
|
|
const int lsb = width * element;
|
|
|
|
|
const int msb = lsb + width - 1;
|
2022-07-09 13:28:28 +02:00
|
|
|
V3Number fieldNum{nump, width};
|
2019-05-10 02:03:19 +02:00
|
|
|
fieldNum.opSel(*nump, msb, lsb);
|
2021-06-21 00:32:57 +02:00
|
|
|
const int arrayElem = arrayp->lo() + element;
|
2020-04-15 13:58:34 +02:00
|
|
|
out << arrayElem << " = " << prettyNumber(&fieldNum, childTypep);
|
|
|
|
|
if (element < arrayElements - 1) out << ", ";
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
out << "]";
|
2019-05-19 22:13:13 +02:00
|
|
|
return out.str();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nump->ascii();
|
2017-05-10 00:54:15 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
// Checking METHODS
|
|
|
|
|
public:
|
2012-05-04 03:59:47 +02:00
|
|
|
/// Call other-this function on all new *non-constant* var references
|
2009-07-14 17:24:21 +02:00
|
|
|
virtual void varRefCb(AstVarRef* nodep) {}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
void clearOptimizable(AstNode* nodep /*null ok*/, const string& why) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Something bad found. optimizable() will return false,
|
2019-07-23 19:58:17 +02:00
|
|
|
// and fetchConst should not be called or it may assert.
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_whyNotNodep) {
|
|
|
|
|
m_whyNotNodep = nodep;
|
2024-07-28 20:18:24 +02:00
|
|
|
if (debug() >= 5) { // LCOV_EXCL_START
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO_PREFIX("Clear optimizable: " << why);
|
2023-07-08 19:20:40 +02:00
|
|
|
if (nodep) std::cout << ": " << nodep;
|
|
|
|
|
std::cout << std::endl;
|
2024-07-28 20:18:24 +02:00
|
|
|
} // LCOV_EXCL_STOP
|
2019-05-19 22:13:13 +02:00
|
|
|
m_whyNotOptimizable = why;
|
2018-02-02 03:24:41 +01:00
|
|
|
std::ostringstream stack;
|
2025-08-30 13:45:35 +02:00
|
|
|
int n = 0;
|
2022-07-30 17:52:35 +02:00
|
|
|
for (const auto& callstack : vlstd::reverse_view(m_callStack)) {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstFuncRef* const funcp = callstack->m_funcp;
|
2023-09-23 14:52:50 +02:00
|
|
|
stack << "\n " << funcp->fileline() << "... Called from '"
|
|
|
|
|
<< funcp->prettyName() << "()' with parameters:";
|
2022-04-18 19:03:56 +02:00
|
|
|
V3TaskConnects* tconnects = callstack->m_tconnects;
|
2019-05-19 22:13:13 +02:00
|
|
|
for (V3TaskConnects::iterator conIt = tconnects->begin();
|
|
|
|
|
conIt != tconnects->end(); ++conIt) {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstVar* const portp = conIt->first;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const pinp = conIt->second->exprp();
|
2025-08-20 19:21:24 +02:00
|
|
|
const AstNodeDType* const dtypep = pinp->dtypep();
|
2022-07-30 17:52:35 +02:00
|
|
|
if (AstConst* const valp = fetchConstNull(pinp)) {
|
2021-03-19 23:44:26 +01:00
|
|
|
stack << "\n " << portp->prettyName() << " = "
|
|
|
|
|
<< prettyNumber(&valp->num(), dtypep);
|
2022-07-30 17:52:35 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-08-30 13:45:35 +02:00
|
|
|
if (++n > CALL_STACK_MAX) {
|
|
|
|
|
stack << "\n ... stack truncated";
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_whyNotOptimizable += stack.str();
|
|
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
bool optimizable() const { return m_whyNotNodep == nullptr; }
|
2009-07-17 20:13:11 +02:00
|
|
|
string whyNotMessage() const { return m_whyNotOptimizable; }
|
|
|
|
|
AstNode* whyNotNodep() const { return m_whyNotNodep; }
|
|
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
bool isAssignDly() const { return m_anyAssignDly; }
|
2024-07-02 15:22:32 +02:00
|
|
|
bool isImpure() const { return m_isImpure; }
|
2024-10-11 03:22:06 +02:00
|
|
|
bool isCoverage() const { return m_isCoverage; }
|
2009-07-14 17:24:21 +02:00
|
|
|
int instrCount() const { return m_instrCount; }
|
|
|
|
|
int dataCount() const { return m_dataCount; }
|
|
|
|
|
|
|
|
|
|
// Simulation METHODS
|
|
|
|
|
private:
|
2019-08-09 10:12:49 +02:00
|
|
|
AstConst* allocConst(AstNode* nodep) {
|
2025-01-03 11:33:29 +01:00
|
|
|
// Allocate a constant with this dtype. Reuse them across a 'clear()' call for efficiency.
|
|
|
|
|
return m_constps[nodep->dtypep()].allocate(m_constGeneration, nodep);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-11-10 00:31:24 +01:00
|
|
|
public:
|
2022-11-13 21:33:11 +01:00
|
|
|
void newValue(AstNode* nodep, const AstNodeExpr* valuep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstConst* const constp = VN_CAST(valuep, Const)) {
|
2019-11-10 00:31:24 +01:00
|
|
|
newConst(nodep)->num().opAssign(constp->num());
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " new val " << valuep->name() << " on " << nodep);
|
2019-11-10 00:31:24 +01:00
|
|
|
} else if (fetchValueNull(nodep) != valuep) {
|
|
|
|
|
// const_cast, as clonep() is set on valuep, but nothing should care
|
2022-11-13 21:33:11 +01:00
|
|
|
setValue(nodep, newTrackedClone(const_cast<AstNodeExpr*>(valuep)));
|
2019-11-10 00:31:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
void newOutValue(AstNode* nodep, const AstNodeExpr* valuep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstConst* const constp = VN_CAST(valuep, Const)) {
|
2019-11-10 00:31:24 +01:00
|
|
|
newOutConst(nodep)->num().opAssign(constp->num());
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " new oval " << valuep->name() << " on " << nodep);
|
2019-11-10 00:31:24 +01:00
|
|
|
} else if (fetchOutValueNull(nodep) != valuep) {
|
|
|
|
|
// const_cast, as clonep() is set on valuep, but nothing should care
|
2022-11-13 21:33:11 +01:00
|
|
|
setOutValue(nodep, newTrackedClone(const_cast<AstNodeExpr*>(valuep)));
|
2019-11-10 00:31:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-07-23 19:58:17 +02:00
|
|
|
private:
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newTrackedClone(AstNodeExpr* nodep) {
|
|
|
|
|
AstNodeExpr* const newp = nodep->cloneTree(false);
|
2019-11-10 00:31:24 +01:00
|
|
|
m_reclaimValuesp.push_back(newp);
|
|
|
|
|
return newp;
|
|
|
|
|
}
|
2019-11-09 19:33:54 +01:00
|
|
|
AstConst* newConst(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Set a constant value for this node
|
2023-10-29 02:12:27 +02:00
|
|
|
if (!VN_IS(m_varAux(nodep).valuep, Const)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = allocConst(nodep);
|
2024-09-26 17:31:47 +02:00
|
|
|
m_varAux(nodep).valuep = constp;
|
2019-07-23 19:58:17 +02:00
|
|
|
return constp;
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2019-08-09 10:12:49 +02:00
|
|
|
return fetchConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2019-11-09 19:33:54 +01:00
|
|
|
AstConst* newOutConst(AstNode* nodep) {
|
2019-11-10 00:31:24 +01:00
|
|
|
// Set a var-output constant value for this node
|
2023-10-29 02:12:27 +02:00
|
|
|
if (!VN_IS(m_varAux(nodep).outValuep, Const)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = allocConst(nodep);
|
2024-09-26 17:31:47 +02:00
|
|
|
m_varAux(nodep).outValuep = constp;
|
2019-07-23 19:58:17 +02:00
|
|
|
return constp;
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2019-08-09 10:12:49 +02:00
|
|
|
return fetchOutConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-11-10 00:31:24 +01:00
|
|
|
public:
|
2023-10-29 02:12:27 +02:00
|
|
|
AstNodeExpr* fetchValueNull(AstNode* nodep) { return m_varAux(nodep).valuep; }
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-11-10 00:31:24 +01:00
|
|
|
private:
|
2023-10-29 02:12:27 +02:00
|
|
|
AstNodeExpr* fetchOutValueNull(AstNode* nodep) { return m_varAux(nodep).outValuep; }
|
2020-04-15 13:58:34 +02:00
|
|
|
AstConst* fetchConstNull(AstNode* nodep) { return VN_CAST(fetchValueNull(nodep), Const); }
|
2019-07-23 19:58:17 +02:00
|
|
|
AstConst* fetchOutConstNull(AstNode* nodep) {
|
2019-11-09 19:33:54 +01:00
|
|
|
return VN_CAST(fetchOutValueNull(nodep), Const);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* fetchValue(AstNode* nodep) {
|
|
|
|
|
AstNodeExpr* const valuep = fetchValueNull(nodep);
|
2019-11-10 00:31:24 +01:00
|
|
|
UASSERT_OBJ(valuep, nodep, "No value found for node.");
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(9, " fetch val " << *valuep << " on " << nodep);
|
2019-11-10 00:31:24 +01:00
|
|
|
return valuep;
|
|
|
|
|
}
|
2019-07-23 19:58:17 +02:00
|
|
|
AstConst* fetchConst(AstNode* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = fetchConstNull(nodep);
|
2019-07-23 19:58:17 +02:00
|
|
|
UASSERT_OBJ(constp, nodep, "No value found for node.");
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(9, " fetch num " << *constp << " on " << nodep);
|
2019-07-23 19:58:17 +02:00
|
|
|
return constp;
|
|
|
|
|
}
|
|
|
|
|
AstConst* fetchOutConst(AstNode* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = fetchOutConstNull(nodep);
|
2019-07-23 19:58:17 +02:00
|
|
|
UASSERT_OBJ(constp, nodep, "No value found for node.");
|
|
|
|
|
return constp;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-07-23 19:58:17 +02:00
|
|
|
public:
|
|
|
|
|
V3Number* fetchNumberNull(AstNode* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = fetchConstNull(nodep);
|
2019-11-09 19:33:54 +01:00
|
|
|
if (constp) return &constp->num();
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2019-07-23 19:58:17 +02:00
|
|
|
V3Number* fetchOutNumberNull(AstNode* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = fetchOutConstNull(nodep);
|
2019-11-09 19:33:54 +01:00
|
|
|
if (constp) return &constp->num();
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
private:
|
2023-10-29 02:12:27 +02:00
|
|
|
void setValue(AstNode* nodep, AstNodeExpr* valuep) {
|
2019-11-10 00:31:24 +01:00
|
|
|
UASSERT_OBJ(valuep, nodep, "Simulate setting null value");
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " set val " << valuep->name() << " on " << nodep);
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(nodep).valuep = valuep;
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2023-10-29 02:12:27 +02:00
|
|
|
void setOutValue(AstNode* nodep, AstNodeExpr* valuep) {
|
2019-11-10 00:31:24 +01:00
|
|
|
UASSERT_OBJ(valuep, nodep, "Simulate setting null value");
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " set oval " << valuep->name() << " on " << nodep);
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(nodep).outValuep = valuep;
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-09 13:55:46 +02:00
|
|
|
void checkNodeInfo(AstNode* nodep, bool ignorePredict = false) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_checkOnly) {
|
|
|
|
|
m_instrCount += nodep->instrCount();
|
|
|
|
|
m_dataCount += nodep->width();
|
|
|
|
|
}
|
2022-07-09 13:55:46 +02:00
|
|
|
if (!ignorePredict && !nodep->isPredictOptimizable()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(9, " !predictopt " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
clearOptimizable(nodep, "Isn't predictable");
|
|
|
|
|
}
|
2024-07-02 15:22:32 +02:00
|
|
|
if (!nodep->isPure()) m_isImpure = true;
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-16 00:33:02 +02:00
|
|
|
void knownBadNodeType(AstNode* nodep) {
|
|
|
|
|
// Call for node types we know we can't handle
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (optimizable()) {
|
2024-01-29 02:24:28 +01:00
|
|
|
clearOptimizable(nodep, "Known unhandled node type "s + nodep->typeName());
|
2023-09-16 00:33:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
void badNodeType(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Call for default node types, or other node types we don't know how to handle
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (optimizable()) {
|
|
|
|
|
// Hmm, what is this then?
|
|
|
|
|
// In production code, we'll just not optimize. It should be fixed though.
|
2020-04-15 13:58:34 +02:00
|
|
|
clearOptimizable(nodep,
|
|
|
|
|
"Unknown node type, perhaps missing visitor in SimulateVisitor");
|
2009-07-17 20:13:11 +02:00
|
|
|
#ifdef VL_DEBUG
|
2024-03-03 17:10:19 +01:00
|
|
|
static std::set<VNType> s_typePrinted;
|
|
|
|
|
const auto pair = s_typePrinted.emplace(nodep->type());
|
|
|
|
|
if (pair.second)
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(0, "Unknown node type in SimulateVisitor: " << nodep->prettyTypeName());
|
2009-07-17 20:13:11 +02:00
|
|
|
#endif
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-02 05:16:02 +02:00
|
|
|
AstNode* varOrScope(AstVarRef* nodep) const {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode* vscp;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_scoped) {
|
|
|
|
|
vscp = nodep->varScopep();
|
|
|
|
|
} else {
|
|
|
|
|
vscp = nodep->varp();
|
|
|
|
|
}
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(vscp, nodep, "Not linked");
|
2019-05-19 22:13:13 +02:00
|
|
|
return vscp;
|
2009-07-16 21:30:34 +02:00
|
|
|
}
|
2025-07-23 18:51:16 +02:00
|
|
|
|
|
|
|
|
// True if current node might be jumped over - all visitors must call this up front
|
2025-09-29 16:25:25 +02:00
|
|
|
bool jumpingOver() const { return m_jumptargetp; }
|
2025-08-19 23:02:10 +02:00
|
|
|
void assignOutValue(const AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep, AssignDly)) {
|
2019-07-23 19:58:17 +02:00
|
|
|
// Don't do setValue, as value isn't yet visible to following statements
|
2019-11-10 00:31:24 +01:00
|
|
|
newOutValue(vscp, valuep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2019-11-10 00:31:24 +01:00
|
|
|
newValue(vscp, valuep);
|
|
|
|
|
newOutValue(vscp, valuep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2015-05-09 20:01:54 +02:00
|
|
|
}
|
2010-02-14 16:01:21 +01:00
|
|
|
|
2025-08-22 03:44:31 +02:00
|
|
|
string toStringRecurse(AstNodeExpr* nodep) {
|
|
|
|
|
// Return string representation, or clearOptimizable
|
|
|
|
|
const AstNodeExpr* const valuep = fetchValue(nodep);
|
|
|
|
|
if (const AstConst* const avaluep = VN_CAST(valuep, Const)) {
|
|
|
|
|
return "'h"s + avaluep->num().displayed(nodep, "%0x");
|
|
|
|
|
}
|
|
|
|
|
if (const AstInitArray* const avaluep = VN_CAST(valuep, InitArray)) {
|
|
|
|
|
string result = "'{";
|
|
|
|
|
string comma;
|
|
|
|
|
if (VN_IS(nodep->dtypep(), AssocArrayDType)) {
|
|
|
|
|
if (avaluep->defaultp()) {
|
|
|
|
|
result += comma + "default:" + toStringRecurse(avaluep->defaultp());
|
|
|
|
|
comma = ", ";
|
|
|
|
|
}
|
|
|
|
|
const auto& mapr = avaluep->map();
|
|
|
|
|
for (const auto& itr : mapr) {
|
|
|
|
|
result += comma + cvtToStr(itr.first) + ":"
|
|
|
|
|
+ toStringRecurse(itr.second->valuep());
|
|
|
|
|
comma = ", ";
|
|
|
|
|
}
|
|
|
|
|
} else if (const AstUnpackArrayDType* const dtypep
|
|
|
|
|
= VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
|
|
|
|
|
for (int n = 0; n < dtypep->elementsConst(); ++n) {
|
|
|
|
|
result += comma + toStringRecurse(avaluep->getIndexDefaultedValuep(n));
|
|
|
|
|
comma = ", ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result += "}";
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
clearOptimizable(nodep, "Cannot convert to string"); // LCOV_EXCL_LINE
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-30 13:45:35 +02:00
|
|
|
void initVar(AstVar* nodep) {
|
|
|
|
|
if (const AstBasicDType* const basicp = nodep->dtypeSkipRefp()->basicp()) {
|
|
|
|
|
AstConst cnst{nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0};
|
|
|
|
|
if (basicp->isZeroInit()) {
|
|
|
|
|
cnst.num().setAllBits0();
|
|
|
|
|
} else {
|
|
|
|
|
cnst.num().setAllBitsX();
|
|
|
|
|
}
|
|
|
|
|
newValue(nodep, &cnst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlways* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenTree* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Sensitivities aren't inputs per se; we'll keep our tree under the same sens.
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->varp(), nodep, "Unlinked");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep->varp());
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const vscp = varOrScope(nodep);
|
2009-07-21 20:31:16 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// We can't have non-delayed assignments with same value on LHS and RHS
|
|
|
|
|
// as we don't figure out variable ordering.
|
|
|
|
|
// Delayed is OK though, as we'll decode the next state separately.
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType)
|
|
|
|
|
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), PackArrayDType)
|
2019-11-10 00:31:24 +01:00
|
|
|
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), UnpackArrayDType)
|
2023-01-28 04:41:12 +01:00
|
|
|
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), NodeUOrStructDType))
|
2019-05-19 22:13:13 +02:00
|
|
|
clearOptimizable(nodep, "Array references/not basic");
|
2020-11-07 16:37:55 +01:00
|
|
|
if (nodep->access().isWriteOrRW()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_inDlyAssign) {
|
2023-10-29 02:12:27 +02:00
|
|
|
if (!(m_varAux(vscp).usage & VU_LVDLY)) {
|
|
|
|
|
m_varAux(vscp).usage |= VU_LVDLY;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_checkOnly) varRefCb(nodep);
|
|
|
|
|
}
|
2018-10-06 02:20:28 +02:00
|
|
|
} else { // nondly asn
|
2023-10-29 02:12:27 +02:00
|
|
|
if (!(m_varAux(vscp).usage & VU_LV)) {
|
|
|
|
|
if (!m_params && (m_varAux(vscp).usage & VU_RV)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
clearOptimizable(nodep, "Var read & write");
|
|
|
|
|
}
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(vscp).usage |= VarUsage::VU_LV;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_checkOnly) varRefCb(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-07 16:37:55 +01:00
|
|
|
}
|
|
|
|
|
if (nodep->access().isReadOrRW()) {
|
2023-10-29 02:12:27 +02:00
|
|
|
if (!(m_varAux(vscp).usage & VU_RV)) {
|
|
|
|
|
if (!m_params && (m_varAux(vscp).usage & VU_LV)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
clearOptimizable(nodep, "Var write & read");
|
|
|
|
|
}
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux(vscp).usage |= VU_RV;
|
2024-01-18 01:48:07 +01:00
|
|
|
const bool varIsConst = (nodep->varp()->isConst() || nodep->varp()->isParam())
|
|
|
|
|
&& nodep->varp()->valuep();
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstNodeExpr* const valuep
|
2024-01-18 01:48:07 +01:00
|
|
|
= varIsConst ? fetchValueNull(nodep->varp()->valuep()) : nullptr;
|
2022-11-13 21:33:11 +01:00
|
|
|
// Propagate PARAM constants for constant function analysis
|
2024-01-18 01:48:07 +01:00
|
|
|
if (varIsConst && valuep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_checkOnly && optimizable()) newValue(vscp, valuep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
if (m_checkOnly) varRefCb(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-06 02:20:28 +02:00
|
|
|
if (!m_checkOnly && optimizable()) { // simulating
|
2020-10-31 03:28:51 +01:00
|
|
|
UASSERT_OBJ(nodep->access().isReadOnly(), nodep,
|
2019-07-06 18:57:50 +02:00
|
|
|
"LHS varref should be handled in AstAssign visitor.");
|
|
|
|
|
{
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return simulation value - copy by reference instead of value for speed
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* valuep = fetchValueNull(vscp);
|
2019-11-10 00:31:24 +01:00
|
|
|
if (!valuep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_params) {
|
2020-04-15 13:58:34 +02:00
|
|
|
clearOptimizable(
|
|
|
|
|
nodep, "Language violation: reference to non-function-local variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3fatalSrc(
|
|
|
|
|
"Variable value should have been set before any visitor called.");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2019-11-10 00:31:24 +01:00
|
|
|
valuep = allocConst(nodep); // Any value; just so recover from error
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2019-11-10 00:31:24 +01:00
|
|
|
setValue(nodep, valuep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarXRef* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_scoped) {
|
|
|
|
|
badNodeType(nodep);
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
clearOptimizable(nodep, "Language violation: Dotted hierarchical references not "
|
|
|
|
|
"allowed in constant functions");
|
|
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_params) {
|
|
|
|
|
badNodeType(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->dpiImport()) {
|
2021-08-21 16:33:20 +02:00
|
|
|
if (m_params) {
|
2024-03-02 15:05:21 +01:00
|
|
|
nodep->v3error("Constant function may not be DPI import (IEEE 1800-2023 13.4.3)");
|
2021-08-21 16:33:20 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
clearOptimizable(nodep, "DPI import functions aren't simulatable");
|
|
|
|
|
}
|
2021-08-21 16:33:20 +02:00
|
|
|
if (nodep->underGenerate()) {
|
|
|
|
|
nodep->v3error(
|
2024-03-02 15:05:21 +01:00
|
|
|
"Constant function may not be declared under generate (IEEE 1800-2023 13.4.3)");
|
2021-08-21 16:33:20 +02:00
|
|
|
clearOptimizable(nodep, "Constant function called under generate");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2023-01-23 14:35:10 +01:00
|
|
|
}
|
|
|
|
|
void visit(AstInitialStatic* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2023-01-23 14:35:10 +01:00
|
|
|
if (!m_params) {
|
|
|
|
|
badNodeType(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " IF " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->condp());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (optimizable()) {
|
2019-07-23 19:58:17 +02:00
|
|
|
if (fetchConst(nodep->condp())->num().isNeqZero()) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->thensp());
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->elsesp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_checkOnly && optimizable()) newValue(nodep, nodep);
|
2019-11-10 00:31:24 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitArray* nodep) override {
|
2019-11-10 00:31:24 +01:00
|
|
|
checkNodeInfo(nodep);
|
2024-07-19 14:56:30 +02:00
|
|
|
iterateChildrenConst(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_checkOnly && optimizable()) newValue(nodep, nodep);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2024-07-19 14:56:30 +02:00
|
|
|
void visit(AstInitItem* nodep) override {
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEnumItemRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const valuep = nodep->itemp()->valuep();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (valuep) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(valuep);
|
2023-12-24 16:17:44 +01:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
newValue(nodep, fetchValue(valuep));
|
2015-10-02 03:15:01 +02:00
|
|
|
} else {
|
2020-05-23 16:34:58 +02:00
|
|
|
clearOptimizable(nodep, "No value found for enum item"); // LCOV_EXCL_LINE
|
2015-10-02 03:15:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeUniop* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeBiop* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
2025-06-24 17:59:09 +02:00
|
|
|
AstConst* const valuep = newConst(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
2019-07-23 19:58:17 +02:00
|
|
|
fetchConst(nodep->rhsp())->num());
|
2025-06-24 17:59:09 +02:00
|
|
|
// See #5490. 'numberOperate' on partially out of range select yields 'x' bits,
|
|
|
|
|
// but in reality it would yield '0's without V3Table, so force 'x' bits to '0',
|
|
|
|
|
// to ensure the result is the same with and without V3Table.
|
|
|
|
|
if (!m_params && VN_IS(nodep, Sel) && valuep->num().isAnyX()) {
|
|
|
|
|
V3Number num{valuep, valuep->width(), valuep->num()};
|
|
|
|
|
valuep->num().opBitsOne(num);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeTriop* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
2019-07-23 19:58:17 +02:00
|
|
|
fetchConst(nodep->rhsp())->num(),
|
|
|
|
|
fetchConst(nodep->thsp())->num());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeQuadop* nodep) override {
|
2020-05-10 20:27:22 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2020-05-10 20:27:22 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
|
|
|
|
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
|
|
|
|
fetchConst(nodep->rhsp())->num(),
|
|
|
|
|
fetchConst(nodep->thsp())->num(),
|
|
|
|
|
fetchConst(nodep->fhsp())->num());
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstLogAnd* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Need to short circuit
|
|
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->lhsp());
|
2023-12-24 16:17:44 +01:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
if (fetchConst(nodep->lhsp())->num().isNeqZero()) {
|
|
|
|
|
iterateConst(nodep->rhsp());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
newValue(nodep, fetchValue(nodep->rhsp()));
|
|
|
|
|
} else {
|
|
|
|
|
newValue(nodep, fetchValue(nodep->lhsp())); // a zero
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-12-22 14:33:16 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstLogOr* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Need to short circuit
|
|
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->lhsp());
|
2023-12-24 16:17:44 +01:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
if (fetchConst(nodep->lhsp())->num().isNeqZero()) {
|
|
|
|
|
newValue(nodep, fetchValue(nodep->lhsp())); // a one
|
|
|
|
|
} else {
|
|
|
|
|
iterateConst(nodep->rhsp());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
newValue(nodep, fetchValue(nodep->rhsp()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-12-22 14:33:16 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstLogIf* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Need to short circuit, same as (!A || B)
|
|
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->lhsp());
|
2023-12-24 16:17:44 +01:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
if (fetchConst(nodep->lhsp())->num().isEqZero()) {
|
|
|
|
|
const AstConst cnst{nodep->fileline(), AstConst::WidthedValue{}, 1, 1}; // a one
|
|
|
|
|
newValue(nodep, &cnst); // a one
|
|
|
|
|
} else {
|
|
|
|
|
iterateConst(nodep->rhsp());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
newValue(nodep, fetchValue(nodep->rhsp()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-12-22 14:33:16 +01:00
|
|
|
}
|
2025-08-16 00:49:06 +02:00
|
|
|
void visit(AstCond* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We could use above visit(AstNodeTriop), but need to do short circuiting.
|
|
|
|
|
// It's also slower even O(n^2) to evaluate both sides when we
|
|
|
|
|
// really only need to evaluate one side.
|
|
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->condp());
|
2023-11-11 20:47:54 +01:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
if (fetchConst(nodep->condp())->num().isNeqZero()) {
|
|
|
|
|
iterateConst(nodep->thenp());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
newValue(nodep, fetchValue(nodep->thenp()));
|
|
|
|
|
} else {
|
|
|
|
|
iterateConst(nodep->elsep());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
newValue(nodep, fetchValue(nodep->elsep()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2015-11-12 02:49:45 +01:00
|
|
|
|
2025-06-28 23:12:03 +02:00
|
|
|
void handleAssignArray(AstNodeAssign* nodep, AstArraySel* selp, AstNodeExpr* valueFromp) {
|
2019-11-10 00:31:24 +01:00
|
|
|
// At present we only handle single dimensional assignments
|
|
|
|
|
// To do better, we need the concept of lvalues, or similar, to know where/how to insert
|
|
|
|
|
checkNodeInfo(selp);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(selp->bitp()); // Bit index
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarRef* const varrefp = VN_CAST(selp->fromp(), VarRef);
|
2019-11-10 00:31:24 +01:00
|
|
|
if (!varrefp) {
|
2025-06-28 23:12:03 +02:00
|
|
|
clearOptimizable(selp->fromp(), "Array select LHS isn't simple variable");
|
2019-11-10 00:31:24 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
AstUnpackArrayDType* const arrayp
|
|
|
|
|
= VN_AS(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType);
|
2019-11-10 00:31:24 +01:00
|
|
|
UASSERT_OBJ(arrayp, nodep, "Array select of non-array dtype");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstBasicDType* const basicp = VN_CAST(arrayp->subDTypep()->skipRefp(), BasicDType);
|
2019-11-10 00:31:24 +01:00
|
|
|
if (!basicp) {
|
|
|
|
|
clearOptimizable(nodep, "Array of non-basic dtype (e.g. array-of-array)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!m_checkOnly && optimizable()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const vscp = varOrScope(varrefp);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstInitArray* initp = nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstInitArray* const vscpnump = VN_CAST(fetchOutValueNull(vscp), InitArray)) {
|
2019-11-10 00:31:24 +01:00
|
|
|
initp = vscpnump;
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (AstInitArray* const vscpnump = VN_CAST(fetchValueNull(vscp), InitArray)) {
|
2019-11-10 00:31:24 +01:00
|
|
|
initp = vscpnump;
|
|
|
|
|
} else { // Assignment to unassigned variable, all bits are X
|
|
|
|
|
// TODO generic initialization which builds X/arrays by recursion
|
2022-07-09 13:28:28 +02:00
|
|
|
AstConst* const outconstp = new AstConst{
|
|
|
|
|
nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0};
|
2019-11-10 00:31:24 +01:00
|
|
|
if (basicp->isZeroInit()) {
|
|
|
|
|
outconstp->num().setAllBits0();
|
|
|
|
|
} else {
|
|
|
|
|
outconstp->num().setAllBitsX();
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-09 13:28:28 +02:00
|
|
|
initp = new AstInitArray{nodep->fileline(), arrayp, outconstp};
|
2019-11-10 00:31:24 +01:00
|
|
|
m_reclaimValuesp.push_back(initp);
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
const uint32_t index = fetchConst(selp->bitp())->toUInt();
|
2025-06-28 23:12:03 +02:00
|
|
|
AstNodeExpr* const valuep = newTrackedClone(fetchValue(valueFromp));
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " set val[" << index << "] = " << valuep);
|
2019-11-10 00:31:24 +01:00
|
|
|
// Values are in the "real" tree under the InitArray so can eventually extract it,
|
2023-10-29 02:12:27 +02:00
|
|
|
// Not in the usual setValue (via m_varAux)
|
2019-11-10 00:31:24 +01:00
|
|
|
initp->addIndexValuep(index, valuep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, initp, "", "array");
|
2019-11-10 00:31:24 +01:00
|
|
|
assignOutValue(nodep, vscp, initp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-28 23:12:03 +02:00
|
|
|
void handleAssignSel(AstNodeAssign* nodep, AstSel* selp, AstNodeExpr* valueFromp) {
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVarRef* varrefp = nullptr;
|
2022-07-09 13:28:28 +02:00
|
|
|
V3Number lsb{nodep};
|
2020-04-15 13:58:34 +02:00
|
|
|
handleAssignSelRecurse(nodep, selp, varrefp /*ref*/, lsb /*ref*/, 0);
|
2019-05-10 02:03:19 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(varrefp, nodep,
|
|
|
|
|
"Indicated optimizable, but no variable found on RHS of select");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const vscp = varOrScope(varrefp);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstConst* outconstp = nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstConst* const vscpnump = fetchOutConstNull(vscp)) {
|
2019-11-10 00:31:24 +01:00
|
|
|
outconstp = vscpnump;
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (AstConst* const vscpnump = fetchConstNull(vscp)) {
|
2019-11-10 00:31:24 +01:00
|
|
|
outconstp = vscpnump;
|
2019-05-10 02:03:19 +02:00
|
|
|
} else { // Assignment to unassigned variable, all bits are X or 0
|
2022-07-09 13:28:28 +02:00
|
|
|
outconstp = new AstConst{nodep->fileline(), AstConst::WidthedValue{},
|
|
|
|
|
varrefp->varp()->widthMin(), 0};
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) {
|
2019-11-10 00:31:24 +01:00
|
|
|
outconstp->num().setAllBits0();
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2019-11-10 00:31:24 +01:00
|
|
|
outconstp->num().setAllBitsX();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-09-10 23:42:45 +02:00
|
|
|
m_reclaimValuesp.emplace_back(outconstp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-06-28 23:12:03 +02:00
|
|
|
outconstp->num().opSelInto(fetchConst(valueFromp)->num(), lsb, selp->widthConst());
|
2019-11-10 00:31:24 +01:00
|
|
|
assignOutValue(nodep, vscp, outconstp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2017-03-31 01:05:55 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
void handleAssignSelRecurse(AstNodeAssign* nodep, AstSel* selp, AstVarRef*& outVarrefpRef,
|
|
|
|
|
V3Number& lsbRef, int depth) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Recurse down to find final variable being set (outVarrefp), with
|
2019-11-10 00:31:24 +01:00
|
|
|
// lsb to be eventually set on lsbRef
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(selp);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(selp->lsbp()); // Bit index
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVarRef* const varrefp = VN_CAST(selp->fromp(), VarRef)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
outVarrefpRef = varrefp;
|
2019-08-09 10:12:49 +02:00
|
|
|
lsbRef = fetchConst(selp->lsbp())->num();
|
2019-05-19 22:13:13 +02:00
|
|
|
return; // And presumably still optimizable()
|
2023-11-12 19:30:48 +01:00
|
|
|
} else if (AstSel* const subselp = VN_CAST(selp->fromp(), Sel)) {
|
2022-07-09 13:28:28 +02:00
|
|
|
V3Number sublsb{nodep};
|
2020-04-15 13:58:34 +02:00
|
|
|
handleAssignSelRecurse(nodep, subselp, outVarrefpRef, sublsb /*ref*/, depth + 1);
|
2019-05-10 02:03:19 +02:00
|
|
|
if (optimizable()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
lsbRef = sublsb;
|
2019-08-09 10:12:49 +02:00
|
|
|
lsbRef.opAdd(sublsb, fetchConst(selp->lsbp())->num());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-06-28 23:12:03 +02:00
|
|
|
clearOptimizable(selp->fromp(), "Select LHS isn't simple variable");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void handleAssignRecurse(AstNodeAssign* nodep, AstNodeExpr* lhsp, AstNodeExpr* valueFromp) {
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
if (AstArraySel* const selp = VN_CAST(lhsp, ArraySel)) {
|
|
|
|
|
if (!m_params) {
|
|
|
|
|
clearOptimizable(lhsp, "Assign LHS has select");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
handleAssignArray(nodep, selp, valueFromp);
|
|
|
|
|
} else if (AstConcat* const selp = VN_CAST(lhsp, Concat)) {
|
|
|
|
|
checkNodeInfo(selp);
|
2025-08-19 23:02:10 +02:00
|
|
|
if (!VN_IS(selp->rhsp()->dtypep()->skipRefp(), BasicDType)) {
|
2025-06-28 23:12:03 +02:00
|
|
|
clearOptimizable(lhsp, "Assign LHS concat of non-basic type");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Split value into left and right concat values
|
|
|
|
|
if (!m_checkOnly) {
|
|
|
|
|
{
|
|
|
|
|
AstConst* const outconstp
|
|
|
|
|
= new AstConst{selp->lhsp()->fileline(), AstConst::WidthedValue{},
|
|
|
|
|
selp->lhsp()->width(), 0};
|
|
|
|
|
outconstp->num().opSel(fetchConst(valueFromp)->num(),
|
|
|
|
|
selp->lhsp()->width() + selp->rhsp()->width() + 1,
|
|
|
|
|
selp->rhsp()->width());
|
|
|
|
|
newValue(selp->lhsp(), outconstp);
|
2025-09-10 23:42:45 +02:00
|
|
|
VL_DO_DANGLING(outconstp->deleteTree(), outconstp);
|
2025-06-28 23:12:03 +02:00
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
AstConst* const outconstp
|
|
|
|
|
= new AstConst{selp->rhsp()->fileline(), AstConst::WidthedValue{},
|
|
|
|
|
selp->rhsp()->widthMin(), 0};
|
|
|
|
|
outconstp->num().opSel(fetchConst(valueFromp)->num(),
|
|
|
|
|
selp->rhsp()->width() - 1, 0);
|
|
|
|
|
newValue(selp->rhsp(), outconstp);
|
2025-09-10 23:42:45 +02:00
|
|
|
VL_DO_DANGLING(outconstp->deleteTree(), outconstp);
|
2025-06-28 23:12:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handleAssignRecurse(nodep, selp->lhsp(), selp->lhsp());
|
|
|
|
|
handleAssignRecurse(nodep, selp->rhsp(), selp->rhsp());
|
|
|
|
|
} else if (AstReplicate* const selp = VN_CAST(lhsp, Replicate)) {
|
|
|
|
|
checkNodeInfo(selp);
|
|
|
|
|
iterateAndNextConstNull(selp->countp());
|
|
|
|
|
AstConst* const countp = VN_CAST(selp->countp(), Const);
|
|
|
|
|
if (!countp || !countp->num().isEqOne()) {
|
|
|
|
|
clearOptimizable(selp, "Replicate LHS count isn't one");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
handleAssignRecurse(nodep, selp->srcp(), valueFromp);
|
|
|
|
|
} else if (AstSel* const selp = VN_CAST(lhsp, Sel)) {
|
|
|
|
|
if (!m_params) {
|
|
|
|
|
clearOptimizable(lhsp, "Assign LHS has select");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
handleAssignSel(nodep, selp, valueFromp);
|
|
|
|
|
} else if (VN_IS(lhsp, VarRef)) {
|
|
|
|
|
if (m_checkOnly) {
|
|
|
|
|
iterateAndNextConstNull(lhsp);
|
|
|
|
|
} else {
|
|
|
|
|
AstNode* const vscp = varOrScope(VN_CAST(lhsp, VarRef));
|
|
|
|
|
assignOutValue(nodep, vscp, fetchValue(valueFromp));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
clearOptimizable(lhsp, "Assign LHS isn't simple variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2015-11-12 02:49:45 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
2022-07-09 13:55:46 +02:00
|
|
|
checkNodeInfo(nodep);
|
2022-11-19 21:23:37 +01:00
|
|
|
|
|
|
|
|
VL_RESTORER(m_inDlyAssign);
|
|
|
|
|
|
2022-01-01 18:24:19 +01:00
|
|
|
if (VN_IS(nodep, AssignForce)) {
|
|
|
|
|
clearOptimizable(nodep, "Force");
|
|
|
|
|
} else if (VN_IS(nodep, AssignDly)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non-dly assigns");
|
|
|
|
|
m_anyAssignDly = true;
|
|
|
|
|
m_inDlyAssign = true;
|
|
|
|
|
} else {
|
|
|
|
|
if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non-dly assigns");
|
|
|
|
|
m_anyAssignComb = true;
|
|
|
|
|
}
|
2015-11-12 02:49:45 +01:00
|
|
|
|
2025-06-28 23:12:03 +02:00
|
|
|
iterateAndNextConstNull(nodep->rhsp()); // Value to assign
|
|
|
|
|
handleAssignRecurse(nodep, nodep->lhsp(), nodep->rhsp());
|
2025-08-30 13:45:35 +02:00
|
|
|
// UINFO(9, "set " << fetchConst(nodep->rhsp())->num().ascii() << " for assign "
|
|
|
|
|
// << nodep->lhsp()->name());
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstArraySel* nodep) override {
|
2019-11-10 00:31:24 +01:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstConst* const indexp = fetchConst(nodep->bitp());
|
2021-11-26 23:55:36 +01:00
|
|
|
const uint32_t offset = indexp->num().toUInt();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(offset);
|
2019-11-10 00:31:24 +01:00
|
|
|
if (!itemp) {
|
|
|
|
|
clearOptimizable(nodep, "Array initialization has too few elements, need element "
|
2020-04-15 13:58:34 +02:00
|
|
|
+ cvtToStr(offset));
|
2025-01-03 11:33:29 +01:00
|
|
|
} else if (AstConst* const constp = VN_CAST(itemp, Const)) {
|
|
|
|
|
setValue(nodep, constp);
|
2019-11-10 00:31:24 +01:00
|
|
|
} else {
|
2024-07-19 14:56:30 +02:00
|
|
|
setValue(nodep, fetchValue(itemp));
|
2019-11-10 00:31:24 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
clearOptimizable(nodep, "Array select of non-array");
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-06 23:29:40 +02:00
|
|
|
|
|
|
|
|
// Evaluate a slice of an unpacked array. If the base value is a constant
|
|
|
|
|
// AstInitArray, build a new AstInitArray representing the slice and assign
|
|
|
|
|
// it as this node's value. New index 0 corresponds to the lowest index of
|
|
|
|
|
// the slice. Otherwise, mark this node as unoptimizable.
|
|
|
|
|
void visit(AstSliceSel* nodep) override {
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
if (m_checkOnly || !optimizable()) return;
|
|
|
|
|
// Fetch the base constant array
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) {
|
2025-08-06 23:29:40 +02:00
|
|
|
const VNumRange& sliceRange = nodep->declRange();
|
|
|
|
|
const uint32_t sliceElements = sliceRange.elements();
|
|
|
|
|
const int sliceLo = sliceRange.lo();
|
|
|
|
|
// Use this node's dtype for the slice array
|
|
|
|
|
AstNodeDType* const dtypep = nodep->dtypep()->skipRefp();
|
|
|
|
|
// Clone the default value from the base array, if present
|
|
|
|
|
AstNodeExpr* defaultp = nullptr;
|
|
|
|
|
if (initp->defaultp()) defaultp = initp->defaultp()->cloneTree(false);
|
|
|
|
|
AstInitArray* const newInitp = new AstInitArray{nodep->fileline(), dtypep, defaultp};
|
|
|
|
|
// Copy slice elements in ascending order
|
|
|
|
|
for (uint32_t idx = 0; idx < sliceElements; ++idx) {
|
|
|
|
|
const uint32_t baseIdx = sliceLo + idx;
|
|
|
|
|
AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(baseIdx);
|
|
|
|
|
if (itemp) newInitp->addIndexValuep(idx, itemp->cloneTree(false));
|
|
|
|
|
}
|
|
|
|
|
// Assign the new constant array and track it for later deletion
|
|
|
|
|
setValue(nodep, newInitp);
|
|
|
|
|
m_reclaimValuesp.push_back(newInitp);
|
|
|
|
|
} else {
|
|
|
|
|
clearOptimizable(nodep, "Slice select of non-array");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBegin* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2009-07-21 20:31:16 +02:00
|
|
|
}
|
2025-09-23 20:49:01 +02:00
|
|
|
void visit(AstCase* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " CASE " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (optimizable()) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->exprp());
|
2019-05-19 22:13:13 +02:00
|
|
|
bool hit = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!itemp->isDefault()) {
|
2019-11-09 17:30:31 +01:00
|
|
|
for (AstNode* ep = itemp->condsp(); ep; ep = ep->nextp()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (hit) break;
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(ep);
|
2019-05-10 02:03:19 +02:00
|
|
|
if (optimizable()) {
|
2022-07-09 13:28:28 +02:00
|
|
|
V3Number match{nodep, 1};
|
2019-07-23 19:58:17 +02:00
|
|
|
match.opEq(fetchConst(nodep->exprp())->num(), fetchConst(ep)->num());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (match.isNeqZero()) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(itemp->stmtsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
hit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Else default match
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (hit) break;
|
2025-08-19 23:02:10 +02:00
|
|
|
if (itemp->isDefault()) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(itemp->stmtsp());
|
2019-05-19 22:13:13 +02:00
|
|
|
hit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCaseItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Real handling is in AstNodeCase
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstComment*) override {}
|
2009-07-17 20:13:11 +02:00
|
|
|
|
2022-11-11 02:49:11 +01:00
|
|
|
void visit(AstStmtExpr* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2022-11-11 02:49:11 +01:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2022-11-11 02:49:11 +01:00
|
|
|
}
|
2023-05-21 18:24:00 +02:00
|
|
|
void visit(AstExprStmt* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2023-05-21 18:24:00 +02:00
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
iterateAndNextConstNull(nodep->resultp());
|
2023-05-27 18:43:40 +02:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
if (!m_checkOnly) newValue(nodep, fetchValue(nodep->resultp()));
|
2023-05-21 18:24:00 +02:00
|
|
|
}
|
2022-11-11 02:49:11 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpBlock* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2025-09-29 16:25:25 +02:00
|
|
|
if (m_jumptargetp == nodep) {
|
2025-07-23 18:51:16 +02:00
|
|
|
UINFO(5, " JUMP DONE " << nodep);
|
2025-09-29 16:25:25 +02:00
|
|
|
m_jumptargetp = nullptr;
|
2025-07-23 18:51:16 +02:00
|
|
|
}
|
2020-05-07 03:33:05 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpGo* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (!m_checkOnly) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " JUMP GO " << nodep);
|
2025-09-29 16:25:25 +02:00
|
|
|
UASSERT_OBJ(!m_jumptargetp, nodep, "Jump inside jump");
|
|
|
|
|
m_jumptargetp = nodep->blockp();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstLoop* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2025-09-29 16:25:25 +02:00
|
|
|
UINFO(5, " LOOP " << nodep);
|
|
|
|
|
// Doing lots of loops is slow, so only for parameters
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_params) {
|
|
|
|
|
badNodeType(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
if (m_checkOnly) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2025-09-29 16:25:25 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
|
|
|
|
|
int loops = 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
UINFO(5, " LOOP-ITER " << nodep);
|
|
|
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
|
|
|
if (jumpingOver()) break;
|
|
|
|
|
|
|
|
|
|
// Prep for next loop
|
|
|
|
|
if (loops++ > v3Global.opt.unrollCountAdjusted(nodep->unroll(), m_params, true)) {
|
|
|
|
|
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an"
|
|
|
|
|
"infinite loop, or use /*verilator unroll_full*/, or "
|
|
|
|
|
"set --unroll-count above "
|
|
|
|
|
+ cvtToStr(loops));
|
|
|
|
|
break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-29 16:25:25 +02:00
|
|
|
|
|
|
|
|
if (m_jumptargetp == nodep) {
|
|
|
|
|
UINFO(5, " LOOP TEST DONE " << nodep);
|
|
|
|
|
m_jumptargetp = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstLoopTest* nodep) override {
|
|
|
|
|
if (jumpingOver()) return;
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
iterateConst(nodep->condp());
|
|
|
|
|
if (!m_checkOnly && optimizable() && fetchConst(nodep->condp())->num().isEqZero()) {
|
|
|
|
|
UINFO(5, " LOOP TEST GO " << nodep);
|
|
|
|
|
UASSERT_OBJ(!m_jumptargetp, nodep, "Jump inside jump");
|
|
|
|
|
m_jumptargetp = nodep->loopp();
|
|
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-29 16:25:25 +02:00
|
|
|
void visit(AstStop* nodep) override {
|
|
|
|
|
if (jumpingOver()) return;
|
|
|
|
|
if (m_params) { // This message seems better than an obscure $stop
|
|
|
|
|
// The spec says $stop is just ignored, it seems evil to ignore assertions
|
|
|
|
|
clearOptimizable(
|
|
|
|
|
nodep,
|
|
|
|
|
"$stop executed during function constification; maybe indicates assertion firing");
|
|
|
|
|
}
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFuncRef* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, " FUNCREF " << nodep);
|
2022-07-09 13:55:46 +02:00
|
|
|
checkNodeInfo(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_params) {
|
|
|
|
|
badNodeType(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-10-22 18:36:58 +02:00
|
|
|
AstNodeFTask* funcp = nodep->taskp();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(funcp, nodep, "Not linked");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_params) V3Width::widthParamsEdit(funcp);
|
|
|
|
|
VL_DANGLING(funcp); // Make sure we've sized the function
|
2021-10-22 18:36:58 +02:00
|
|
|
funcp = nodep->taskp();
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(funcp, nodep, "Not linked");
|
2025-08-30 13:45:35 +02:00
|
|
|
|
2022-01-04 00:50:41 +01:00
|
|
|
if (funcp->recursive()) {
|
2025-08-30 13:45:35 +02:00
|
|
|
if (m_recurseCount >= CONST_FUNC_RECURSION_MAX) {
|
|
|
|
|
clearOptimizable(funcp, "Constant function recursed more than "s
|
|
|
|
|
+ std::to_string(CONST_FUNC_RECURSION_MAX) + " times");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
++m_recurseCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Values from previous call, so can save to stack
|
|
|
|
|
// The "stack" is this visit function's local stack, as this visit is itself recursing
|
|
|
|
|
std::map<AstNode*, AstNodeExpr*> oldValues;
|
|
|
|
|
|
|
|
|
|
if (funcp->recursive() && !m_checkOnly) {
|
|
|
|
|
// Save local automatics
|
|
|
|
|
funcp->foreach([this, &oldValues](AstVar* varp) {
|
|
|
|
|
if (varp->lifetime().isAutomatic()) { // This also does function's I/O
|
|
|
|
|
if (AstNodeExpr* const valuep = fetchValueNull(varp)) {
|
|
|
|
|
AstNodeExpr* const nvaluep = newTrackedClone(valuep);
|
|
|
|
|
oldValues.emplace(varp, nvaluep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// Save every expression value, as might be in middle of expression
|
|
|
|
|
// that calls recursively back to this same function.
|
|
|
|
|
// This is much heavier-weight then likely is needed, in theory
|
|
|
|
|
// we could look at the visit stack to determine what nodes
|
|
|
|
|
// need save-restore, but that is difficult to get right, and
|
|
|
|
|
// recursion is rare.
|
|
|
|
|
funcp->foreach([this, &oldValues](AstNodeExpr* exprp) {
|
|
|
|
|
if (VN_IS(exprp, Const)) return; // Speed up as won't change
|
|
|
|
|
if (AstNodeExpr* const valuep = fetchValueNull(exprp)) {
|
|
|
|
|
AstNodeExpr* const nvaluep = newTrackedClone(valuep);
|
|
|
|
|
oldValues.emplace(exprp, nvaluep);
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-04 00:50:41 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// Apply function call values to function
|
|
|
|
|
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
|
|
|
|
// Must do this in two steps, eval all params, then apply them
|
|
|
|
|
// Otherwise chained functions may have the wrong results
|
2025-08-30 13:45:35 +02:00
|
|
|
std::vector<std::pair<AstVar*, AstNodeExpr*>> portValues;
|
2019-11-09 17:30:31 +01:00
|
|
|
for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const portp = it->first;
|
|
|
|
|
AstNode* const pinp = it->second->exprp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (pinp) { // Else too few arguments in function call - ignore it
|
2018-10-27 23:29:00 +02:00
|
|
|
if (portp->isWritable()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
clearOptimizable(
|
|
|
|
|
portp,
|
|
|
|
|
"Language violation: Outputs/refs not allowed in constant functions");
|
2018-10-27 23:29:00 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Evaluate pin value
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(pinp);
|
2025-08-30 13:45:35 +02:00
|
|
|
// Clone in case are recursing
|
|
|
|
|
portValues.push_back(std::make_pair(portp, newTrackedClone(fetchValue(pinp))));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-30 13:45:35 +02:00
|
|
|
// Apply value to the function
|
|
|
|
|
if (!m_checkOnly && optimizable())
|
|
|
|
|
for (auto& it : portValues) {
|
|
|
|
|
if (!m_checkOnly && optimizable()) newValue(it.first, it.second);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-20 21:06:49 +01:00
|
|
|
SimStackNode stackNode{nodep, &tconnects};
|
2021-07-25 19:38:27 +02:00
|
|
|
// cppcheck-suppress danglingLifetime
|
2021-07-05 16:37:20 +02:00
|
|
|
m_callStack.push_back(&stackNode);
|
2025-08-30 13:45:35 +02:00
|
|
|
if (!m_checkOnly) {
|
|
|
|
|
// Clear output variable
|
|
|
|
|
initVar(VN_CAST(funcp->fvarp(), Var));
|
|
|
|
|
// Clear other automatic variables
|
2025-08-30 14:22:22 +02:00
|
|
|
funcp->foreach([this](AstVar* varp) {
|
2025-08-30 13:45:35 +02:00
|
|
|
if (varp->lifetime().isAutomatic() && !varp->isIO()) initVar(varp);
|
|
|
|
|
});
|
2021-06-01 04:46:41 +02:00
|
|
|
}
|
2025-08-30 13:45:35 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Evaluate the function
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(funcp);
|
2021-07-05 16:37:20 +02:00
|
|
|
m_callStack.pop_back();
|
2025-08-30 13:45:35 +02:00
|
|
|
AstNodeExpr* returnp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_checkOnly && optimizable()) {
|
2025-08-30 13:45:35 +02:00
|
|
|
// Grab return value from output variable
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(funcp->fvarp(), nodep, "Function reference points at non-function");
|
2025-08-30 13:45:35 +02:00
|
|
|
returnp = newTrackedClone(fetchValue(funcp->fvarp()));
|
|
|
|
|
UINFO(5, "func " << nodep->name() << " return = " << returnp);
|
|
|
|
|
}
|
|
|
|
|
// Restore local automatics (none unless recursed)
|
|
|
|
|
for (const auto& it : oldValues) {
|
|
|
|
|
if (it.second) newValue(it.first, it.second);
|
|
|
|
|
}
|
|
|
|
|
if (returnp) newValue(nodep, returnp);
|
|
|
|
|
if (funcp->recursive()) {
|
|
|
|
|
UASSERT_OBJ(m_recurseCount > 0, nodep, "recurse underflow");
|
|
|
|
|
--m_recurseCount;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_params) {
|
|
|
|
|
badNodeType(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScopeName* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ignore
|
2017-03-17 23:40:16 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
2022-07-09 13:55:46 +02:00
|
|
|
checkNodeInfo(nodep);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_params) {
|
|
|
|
|
AstNode* nextArgp = nodep->exprsp();
|
2015-10-24 00:13:25 +02:00
|
|
|
|
2018-10-14 05:06:36 +02:00
|
|
|
string result;
|
2021-06-21 00:32:57 +02:00
|
|
|
const string format = nodep->text();
|
2020-08-16 17:43:49 +02:00
|
|
|
auto pos = format.cbegin();
|
2019-05-19 22:13:13 +02:00
|
|
|
bool inPct = false;
|
2023-02-09 16:09:00 +01:00
|
|
|
string width;
|
2020-08-28 00:48:26 +02:00
|
|
|
for (; pos != format.cend(); ++pos) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!inPct && pos[0] == '%') {
|
|
|
|
|
inPct = true;
|
2023-02-09 16:09:00 +01:00
|
|
|
width = "";
|
2018-10-06 02:20:28 +02:00
|
|
|
} else if (!inPct) { // Normal text
|
2019-05-19 22:13:13 +02:00
|
|
|
result += *pos;
|
2018-10-06 02:20:28 +02:00
|
|
|
} else { // Format character
|
2023-02-09 16:09:00 +01:00
|
|
|
if (std::isdigit(pos[0])) {
|
|
|
|
|
width += pos[0];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
inPct = false;
|
|
|
|
|
|
2023-02-11 02:32:35 +01:00
|
|
|
if (V3Number::displayedFmtLegal(std::tolower(pos[0]), false)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const argp = nextArgp;
|
2019-05-19 22:13:13 +02:00
|
|
|
nextArgp = nextArgp->nextp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = fetchConstNull(argp);
|
2019-07-23 19:58:17 +02:00
|
|
|
if (!constp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
clearOptimizable(
|
|
|
|
|
nodep, "Argument for $display like statement is not constant");
|
2019-05-19 22:13:13 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2024-01-29 02:24:28 +01:00
|
|
|
const string pformat = "%"s + width + pos[0];
|
2020-02-04 05:21:56 +01:00
|
|
|
result += constp->num().displayed(nodep, pformat);
|
2019-05-10 02:03:19 +02:00
|
|
|
} else {
|
2023-02-11 02:32:35 +01:00
|
|
|
switch (std::tolower(pos[0])) {
|
2019-11-09 17:30:31 +01:00
|
|
|
case '%': result += "%"; break;
|
2019-05-19 22:13:13 +02:00
|
|
|
case 'm':
|
|
|
|
|
// This happens prior to AstScope so we don't
|
|
|
|
|
// know the scope name. Leave the %m in place.
|
|
|
|
|
result += "%m";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
clearOptimizable(nodep, "Unknown $display-like format code.");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-17 23:40:16 +01:00
|
|
|
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const resultConstp
|
2022-07-09 13:28:28 +02:00
|
|
|
= new AstConst{nodep->fileline(), AstConst::String{}, result};
|
2019-07-23 19:58:17 +02:00
|
|
|
setValue(nodep, resultConstp);
|
2019-11-10 00:31:24 +01:00
|
|
|
m_reclaimValuesp.push_back(resultConstp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2015-10-24 00:13:25 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDisplay* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!optimizable()) return; // Accelerate
|
2022-07-09 13:55:46 +02:00
|
|
|
// We ignore isPredictOptimizable as $display is often in constant
|
|
|
|
|
// functions and we want them to work if used with parameters
|
|
|
|
|
checkNodeInfo(nodep, /*display:*/ true);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_params) {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstConst* const textp = fetchConst(nodep->fmtp());
|
2019-05-19 22:13:13 +02:00
|
|
|
switch (nodep->displayType()) {
|
2022-01-02 19:56:40 +01:00
|
|
|
case VDisplayType::DT_DISPLAY: // FALLTHRU
|
|
|
|
|
case VDisplayType::DT_INFO: v3warn(USERINFO, textp->name()); break;
|
|
|
|
|
case VDisplayType::DT_ERROR: v3warn(USERERROR, textp->name()); break;
|
|
|
|
|
case VDisplayType::DT_WARNING: v3warn(USERWARN, textp->name()); break;
|
|
|
|
|
case VDisplayType::DT_FATAL: v3warn(USERFATAL, textp->name()); break;
|
|
|
|
|
case VDisplayType::DT_WRITE: // FALLTHRU
|
2019-11-09 17:30:31 +01:00
|
|
|
default: clearOptimizable(nodep, "Unexpected display type");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-10-24 00:13:25 +02:00
|
|
|
}
|
2025-08-22 03:09:10 +02:00
|
|
|
void visit(AstToStringN* nodep) override {
|
|
|
|
|
if (jumpingOver()) return;
|
|
|
|
|
if (!optimizable()) return; // Accelerate
|
|
|
|
|
checkNodeInfo(nodep);
|
|
|
|
|
iterateChildrenConst(nodep);
|
2025-08-22 03:44:31 +02:00
|
|
|
if (!optimizable()) return;
|
|
|
|
|
std::string result = toStringRecurse(nodep->lhsp());
|
|
|
|
|
if (!optimizable()) return;
|
|
|
|
|
AstConst* const resultConstp = new AstConst{nodep->fileline(), AstConst::String{}, result};
|
|
|
|
|
setValue(nodep, resultConstp);
|
|
|
|
|
m_reclaimValuesp.push_back(resultConstp);
|
2025-08-22 03:09:10 +02:00
|
|
|
}
|
2015-10-24 00:13:25 +02:00
|
|
|
|
2024-10-11 03:22:06 +02:00
|
|
|
void visit(AstCoverInc* nodep) override { m_isCoverage = true; }
|
2024-02-08 01:56:32 +01:00
|
|
|
|
2023-09-16 00:33:02 +02:00
|
|
|
// ====
|
|
|
|
|
// Known Bad
|
|
|
|
|
void visit(AstCMethodHard* nodep) override {
|
|
|
|
|
// Some CMethods such as size() on queues could be supported, but
|
|
|
|
|
// instead we should change those methods to new Ast types so we can
|
|
|
|
|
// properly dispatch them
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2023-09-16 00:33:02 +02:00
|
|
|
knownBadNodeType(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstMemberSel* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2023-09-16 00:33:02 +02:00
|
|
|
knownBadNodeType(nodep);
|
|
|
|
|
}
|
|
|
|
|
// ====
|
2009-07-14 17:24:21 +02:00
|
|
|
// default
|
2019-09-09 13:50:21 +02:00
|
|
|
// These types are definitely not reducible
|
2019-11-10 00:31:24 +01:00
|
|
|
// AstCoverInc, AstFinish,
|
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
|
|
|
// AstRand, AstTime, AstCCall, AstCStmt*, AstCExpr*
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2025-07-23 18:51:16 +02:00
|
|
|
if (jumpingOver()) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
badNodeType(nodep);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
|
|
|
|
|
2010-02-14 16:01:21 +01:00
|
|
|
private:
|
|
|
|
|
// MEMBERS - called by constructor
|
|
|
|
|
void setMode(bool scoped, bool checkOnly, bool params) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_checkOnly = checkOnly;
|
|
|
|
|
m_scoped = scoped;
|
|
|
|
|
m_params = params;
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
|
|
|
|
void mainGuts(AstNode* nodep) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2025-09-29 16:25:25 +02:00
|
|
|
UASSERT_OBJ(!m_jumptargetp, m_jumptargetp, "Jump target was not found");
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2009-07-14 17:24:21 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2009-07-17 20:13:11 +02:00
|
|
|
SimulateVisitor() {
|
2018-10-06 01:03:28 +02:00
|
|
|
// Note AstUser#InUse ensures only one invocation exists at once
|
2019-05-19 22:13:13 +02:00
|
|
|
setMode(false, false, false);
|
2018-10-06 02:20:28 +02:00
|
|
|
clear(); // We reuse this structure in the main loop, so put initializers inside clear()
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
|
|
|
|
void clear() {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_whyNotOptimizable = "";
|
2020-08-15 16:12:55 +02:00
|
|
|
m_whyNotNodep = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_anyAssignComb = false;
|
|
|
|
|
m_anyAssignDly = false;
|
|
|
|
|
m_inDlyAssign = false;
|
2024-07-02 15:22:32 +02:00
|
|
|
m_isImpure = false;
|
2024-10-11 03:22:06 +02:00
|
|
|
m_isCoverage = false;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_instrCount = 0;
|
|
|
|
|
m_dataCount = 0;
|
2025-09-29 16:25:25 +02:00
|
|
|
m_jumptargetp = nullptr;
|
2009-07-14 17:24:21 +02:00
|
|
|
|
2018-10-06 02:20:28 +02:00
|
|
|
AstNode::user1ClearTree();
|
2023-10-29 02:12:27 +02:00
|
|
|
m_varAux.clear();
|
2025-01-03 11:33:29 +01:00
|
|
|
++m_constGeneration;
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void mainTableCheck(AstNode* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
setMode(true /*scoped*/, true /*checking*/, false /*params*/);
|
2019-05-19 22:13:13 +02:00
|
|
|
mainGuts(nodep);
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void mainTableEmulate(AstNode* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
setMode(true /*scoped*/, false /*checking*/, false /*params*/);
|
2019-05-19 22:13:13 +02:00
|
|
|
mainGuts(nodep);
|
2009-07-17 20:13:11 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void mainParamEmulate(AstNode* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
setMode(false /*scoped*/, false /*checking*/, true /*params*/);
|
2019-05-19 22:13:13 +02:00
|
|
|
mainGuts(nodep);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~SimulateVisitor() override {
|
2021-07-05 17:35:24 +02:00
|
|
|
m_constps.clear();
|
2025-09-10 23:42:45 +02:00
|
|
|
std::vector<AstNode*> unusedRootps;
|
|
|
|
|
unusedRootps.reserve(m_reclaimValuesp.size());
|
|
|
|
|
for (AstNode* const nodep : m_reclaimValuesp) {
|
|
|
|
|
if (!nodep->backp()) unusedRootps.emplace_back(nodep);
|
|
|
|
|
}
|
2019-11-10 00:31:24 +01:00
|
|
|
m_reclaimValuesp.clear();
|
2025-09-10 23:42:45 +02:00
|
|
|
for (AstNode* const nodep : unusedRootps) VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2009-07-14 17:24:21 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-10-06 02:20:28 +02:00
|
|
|
#endif // Guard
|