2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Substitute constants and expressions in expr temp's
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
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: 2004-2026 Wilson Snyder
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Subst's Transformations:
|
2006-10-11 17:34:50 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each module:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Search all ASSIGN(WORDSEL(...)) and build what it's assigned to
|
|
|
|
|
// Later usages of that word may then be replaced as long as
|
|
|
|
|
// the RHS hasn't changed value.
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Subst.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2025-06-23 23:58:26 +02:00
|
|
|
#include "V3Const.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Stats.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
2026-02-01 06:07:13 +01:00
|
|
|
// Data strored for every variable that tracks assignments and usage
|
|
|
|
|
|
|
|
|
|
class SubstVarEntry final {
|
|
|
|
|
friend class SubstValidVisitor;
|
|
|
|
|
|
|
|
|
|
// SubstVarEntry contains a Record for each word, and one extra for the whole variable
|
|
|
|
|
struct Record final {
|
|
|
|
|
// MEMBERS
|
|
|
|
|
AstNodeAssign* m_assignp; // Last assignment to this part
|
|
|
|
|
uint32_t m_step; // Step number of last assignment. 0 means invalid entry.
|
|
|
|
|
bool m_used; // True if consumed. Can be set even if entry is invalid.
|
|
|
|
|
// CONSTRUCTOR
|
|
|
|
|
Record() { invalidate(); }
|
|
|
|
|
// METHODS
|
|
|
|
|
void invalidate() {
|
|
|
|
|
m_assignp = nullptr;
|
|
|
|
|
m_step = 0;
|
|
|
|
|
m_used = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
2006-10-11 16:13:37 +02:00
|
|
|
|
2006-10-11 17:34:50 +02:00
|
|
|
// MEMBERS
|
2026-02-01 06:07:13 +01:00
|
|
|
// Variable this SubstVarEntry tracks
|
|
|
|
|
AstVar* const m_varp;
|
|
|
|
|
// The recrod for whole variable tracking
|
|
|
|
|
Record m_wholeRecord{};
|
|
|
|
|
// A record for each word in the variable
|
|
|
|
|
std::vector<Record> m_wordRecords{static_cast<size_t>(m_varp->widthWords()), Record{}};
|
|
|
|
|
|
|
|
|
|
// METHDOS
|
|
|
|
|
void deleteAssignmentIfUnused(Record& record, size_t& nAssignDeleted) {
|
|
|
|
|
if (!record.m_assignp) return;
|
|
|
|
|
if (record.m_used) return;
|
|
|
|
|
++nAssignDeleted;
|
|
|
|
|
VL_DO_DANGLING(record.m_assignp->unlinkFrBack()->deleteTree(), record.m_assignp);
|
2006-10-11 16:13:37 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
AstNodeExpr* substRecord(Record& record);
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2006-10-11 17:34:50 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit SubstVarEntry(AstVar* varp)
|
2026-02-01 06:07:13 +01:00
|
|
|
: m_varp{varp} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
~SubstVarEntry() = default;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
// Record assignment of whole variable. The given 'assp' can be null, which means
|
|
|
|
|
// the variable is known to be assigned, but to an unknown value.
|
|
|
|
|
void assignWhole(AstNodeAssign* assp, uint32_t step) {
|
|
|
|
|
// Invalidate all word records
|
|
|
|
|
for (Record& wordRecord : m_wordRecords) wordRecord.invalidate();
|
|
|
|
|
// Set whole record
|
|
|
|
|
m_wholeRecord.m_assignp = assp;
|
|
|
|
|
m_wholeRecord.m_step = step;
|
|
|
|
|
m_wholeRecord.m_used = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
// Like assignWhole word, but records assignment to a specific word of the variable.
|
|
|
|
|
void assignWord(AstNodeAssign* assp, uint32_t step, uint32_t word) {
|
|
|
|
|
// Invalidate whole record
|
|
|
|
|
m_wholeRecord.invalidate();
|
|
|
|
|
// Set word record
|
|
|
|
|
Record& wordRecord = m_wordRecords[word];
|
|
|
|
|
wordRecord.m_assignp = assp;
|
|
|
|
|
wordRecord.m_step = step;
|
|
|
|
|
wordRecord.m_used = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// Mark the whole variable as used (value consumed)
|
|
|
|
|
void usedWhole() {
|
|
|
|
|
m_wholeRecord.m_used = true;
|
|
|
|
|
for (Record& wordRecord : m_wordRecords) wordRecord.m_used = true;
|
2006-10-11 17:34:50 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// Mark the specific word as used (value consumed)
|
|
|
|
|
void usedWord(uint32_t word) {
|
|
|
|
|
m_wholeRecord.m_used = true;
|
|
|
|
|
m_wordRecords[word].m_used = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// Returns substitution of whole word, or nullptr if not known/stale
|
|
|
|
|
AstNodeExpr* substWhole() { return substRecord(m_wholeRecord); }
|
|
|
|
|
// Returns substitution of whole word, or nullptr if not known/stale
|
|
|
|
|
AstNodeExpr* substWord(uint32_t word) { return substRecord(m_wordRecords[word]); }
|
|
|
|
|
|
|
|
|
|
void deleteUnusedAssignments(size_t& nWordAssignDeleted, size_t& nWholeAssignDeleted) {
|
|
|
|
|
// Delete assignments to temporaries if they are not used
|
|
|
|
|
if (!m_varp->isStatementTemp()) return;
|
|
|
|
|
for (Record& wordRecord : m_wordRecords)
|
|
|
|
|
deleteAssignmentIfUnused(wordRecord, nWordAssignDeleted);
|
|
|
|
|
deleteAssignmentIfUnused(m_wholeRecord, nWholeAssignDeleted);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-10-11 17:34:50 +02:00
|
|
|
//######################################################################
|
2026-02-01 06:07:13 +01:00
|
|
|
// See if any variables in the expression we are considering to
|
|
|
|
|
// substitute have changed value since we recorded the assignment.
|
2006-10-11 17:34:50 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
class SubstValidVisitor final : public VNVisitorConst {
|
2006-10-11 17:34:50 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// See SubstVisitor
|
2025-04-27 20:17:24 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
// STATE
|
|
|
|
|
const uint32_t m_step; // Step number where assignment was recorded
|
|
|
|
|
bool m_valid = true; // Is the expression we are considering to substitute valid
|
2006-10-11 17:34:50 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2026-02-01 06:07:13 +01:00
|
|
|
SubstVarEntry& getEntry(AstVarRef* nodep) {
|
|
|
|
|
// This vistor is always invoked on the RHS of an assignment we are considering to
|
|
|
|
|
// substitute. Variable references must have all been recorded when visiting the
|
|
|
|
|
// assignments RHS, so the SubstVarEntry must exist for each referenced variable.
|
|
|
|
|
return *nodep->varp()->user1u().to<SubstVarEntry*>();
|
2006-10-11 17:34:50 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
2006-10-11 17:34:50 +02:00
|
|
|
// VISITORS
|
2026-02-01 06:07:13 +01:00
|
|
|
void visit(AstWordSel* nodep) override {
|
|
|
|
|
if (!m_valid) return;
|
|
|
|
|
|
|
|
|
|
if (AstVarRef* const refp = VN_CAST(nodep->fromp(), VarRef)) {
|
|
|
|
|
if (AstConst* const idxp = VN_CAST(nodep->bitp(), Const)) {
|
|
|
|
|
SubstVarEntry& entry = getEntry(refp);
|
|
|
|
|
// If either the whole variable, or the indexed word was written to
|
|
|
|
|
// after the original assignment was recorded, the value is invalid.
|
|
|
|
|
if (m_step < entry.m_wholeRecord.m_step) m_valid = false;
|
|
|
|
|
if (m_step < entry.m_wordRecords[idxp->toUInt()].m_step) m_valid = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2026-02-01 06:07:13 +01:00
|
|
|
if (!m_valid) return;
|
|
|
|
|
|
|
|
|
|
// If either the whole variable, or any of the words were written to
|
|
|
|
|
// after the original assignment was recorded, the value is invalid.
|
|
|
|
|
SubstVarEntry& entry = getEntry(nodep);
|
|
|
|
|
if (m_step < entry.m_wholeRecord.m_step) {
|
|
|
|
|
m_valid = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (SubstVarEntry::Record& wordRecord : entry.m_wordRecords) {
|
|
|
|
|
if (m_step < wordRecord.m_step) {
|
|
|
|
|
m_valid = false;
|
|
|
|
|
return;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-10-11 17:34:50 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst*) override {} // Accelerate
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
void visit(AstNodeExpr* nodep) override {
|
|
|
|
|
if (!m_valid) return;
|
2023-11-13 00:26:29 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2023-09-09 03:52:33 +02:00
|
|
|
}
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
void visit(AstNode* nodep) override { nodep->v3fatalSrc("Non AstNodeExpr under AstNodeExpr"); }
|
|
|
|
|
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2026-02-01 06:07:13 +01:00
|
|
|
SubstValidVisitor(SubstVarEntry::Record& record)
|
|
|
|
|
: m_step{record.m_step} {
|
|
|
|
|
iterateConst(record.m_assignp->rhsp());
|
|
|
|
|
}
|
|
|
|
|
~SubstValidVisitor() override = default;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static bool valid(SubstVarEntry::Record& record) {
|
|
|
|
|
if (!record.m_assignp) return false;
|
|
|
|
|
return SubstValidVisitor{record}.m_valid;
|
2006-10-11 17:34:50 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
AstNodeExpr* SubstVarEntry::substRecord(SubstVarEntry::Record& record) {
|
|
|
|
|
if (!SubstValidVisitor::valid(record)) return nullptr;
|
|
|
|
|
AstNodeExpr* const rhsp = record.m_assignp->rhsp();
|
|
|
|
|
UDEBUGONLY(UASSERT_OBJ(rhsp->isPure(), record.m_assignp, "Substituting impure expression"););
|
|
|
|
|
return rhsp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2026-02-01 06:07:13 +01:00
|
|
|
// Substitution visitor
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class SubstVisitor final : public VNVisitor {
|
2026-02-01 06:07:13 +01:00
|
|
|
// NODE STATE - only Under AstCFunc
|
|
|
|
|
// AstVar::user1p -> SubstVarEntry* for assignment tracking. Also used by SubstValidVisitor
|
2026-02-01 18:08:49 +01:00
|
|
|
// AstVar::user2 -> bool. Is a constant pool variable
|
|
|
|
|
const VNUser2InUse m_user2InUse;
|
2008-11-21 21:50:33 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2026-02-01 06:07:13 +01:00
|
|
|
std::deque<SubstVarEntry> m_entries; // Storage for SubstVarEntry instances
|
|
|
|
|
uint32_t m_ops = 0; // Number of nodes on the RHS of an assignment
|
|
|
|
|
uint32_t m_assignStep = 0; // Assignment number to determine variable lifetimes
|
2023-11-12 17:01:07 +01:00
|
|
|
const AstCFunc* m_funcp = nullptr; // Current function we are under
|
2026-02-01 06:07:13 +01:00
|
|
|
size_t m_nSubst = 0; // Number of substitutions performed - for avoiding constant folding
|
|
|
|
|
// Statistics
|
|
|
|
|
size_t m_nWordSubstituted = 0; // Number of words substituted
|
|
|
|
|
size_t m_nWholeSubstituted = 0; // Number of whole variables substituted
|
|
|
|
|
size_t m_nWordAssignDeleted = 0; // Number of word assignments deleted
|
|
|
|
|
size_t m_nWholeAssignDeleted = 0; // Number of whole variable assignments deleted
|
2026-02-01 18:08:49 +01:00
|
|
|
size_t m_nConstWordsReinlined = 0; // Number of constant words substituted
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
static constexpr uint32_t SUBST_MAX_OPS_SUBST = 30; // Maximum number of ops to substitute in
|
|
|
|
|
static constexpr uint32_t SUBST_MAX_OPS_NA = 9999; // Not allowed to substitute
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2026-02-01 06:07:13 +01:00
|
|
|
SubstVarEntry& getEntry(AstVarRef* nodep) {
|
2023-11-12 17:01:07 +01:00
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
if (!varp->user1p()) {
|
|
|
|
|
m_entries.emplace_back(varp);
|
|
|
|
|
varp->user1p(&m_entries.back());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
return *varp->user1u().to<SubstVarEntry*>();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
void simplify(AstNodeExpr* exprp) {
|
|
|
|
|
// Often constant, so short circuit
|
|
|
|
|
if (VN_IS(exprp, Const)) return;
|
|
|
|
|
// Iterate expression, then constant fold it if anything changed
|
2025-08-30 16:34:39 +02:00
|
|
|
const size_t nSubstBefore = m_nSubst;
|
2026-02-01 06:07:13 +01:00
|
|
|
exprp = VN_AS(iterateSubtreeReturnEdits(exprp), NodeExpr);
|
|
|
|
|
if (nSubstBefore != m_nSubst) V3Const::constifyEditCpp(exprp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// The way the analysis algorithm works based on statement numbering only works
|
|
|
|
|
// for variables that are written in the same basic block (ignoring AstCond) as
|
|
|
|
|
// where they are consumed. The temporaries introduced in V3Premit are such, so
|
|
|
|
|
// only substitute those for now.
|
|
|
|
|
bool isSubstitutable(AstVar* nodep) { return nodep->isStatementTemp() && !nodep->noSubst(); }
|
|
|
|
|
|
|
|
|
|
void substitute(AstNode* nodep, AstNodeExpr* substp) {
|
2026-02-01 18:08:49 +01:00
|
|
|
AstNodeExpr* newp = substp->backp() ? substp->cloneTreePure(true) : substp;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->isQuad() && newp->isQuad()) {
|
2022-11-19 20:45:33 +01:00
|
|
|
newp = new AstCCast{newp->fileline(), newp, nodep};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->replaceWith(newp);
|
2023-11-12 17:01:07 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2025-08-30 16:34:39 +02:00
|
|
|
++m_nSubst;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// VISITORS
|
2026-02-01 18:08:49 +01:00
|
|
|
|
|
|
|
|
void visit(AstNetlist* nodep) override {
|
|
|
|
|
// Mark constant pool variables
|
|
|
|
|
for (AstNode* np = nodep->constPoolp()->modp()->stmtsp(); np; np = np->nextp()) {
|
|
|
|
|
if (VN_IS(np, Var)) np->user2(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterateAndNextNull(nodep->modulesp());
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
void visit(AstCFunc* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!m_funcp, nodep, "Should not nest");
|
|
|
|
|
UASSERT_OBJ(m_entries.empty(), nodep, "Should not visit outside functions");
|
|
|
|
|
|
|
|
|
|
// Process the function body
|
|
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_funcp);
|
|
|
|
|
m_funcp = nodep;
|
|
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Deletes unused assignments and clear entries
|
|
|
|
|
for (SubstVarEntry& entry : m_entries) {
|
|
|
|
|
entry.deleteUnusedAssignments(m_nWordAssignDeleted, m_nWholeAssignDeleted);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
m_entries.clear();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// Constant fold here, as Ast size can likely be reduced
|
|
|
|
|
if (v3Global.opt.fConstEager()) V3Const::constifyEditCpp(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2023-11-12 17:01:07 +01:00
|
|
|
if (!m_funcp) return;
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
const uint32_t ops = [&]() {
|
|
|
|
|
VL_RESTORER(m_ops);
|
|
|
|
|
m_ops = 0;
|
|
|
|
|
// Simplify the RHS
|
|
|
|
|
simplify(nodep->rhsp());
|
|
|
|
|
// If the became constant, we can continue substituting it
|
|
|
|
|
return VN_IS(nodep->rhsp(), Const) ? 0 : m_ops;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
// Assignment that defines the value, which is either this 'nodep',
|
|
|
|
|
// or nullptr, if the value should not be substituted.
|
|
|
|
|
const auto getAssignp = [&](AstVarRef* refp) -> AstNodeAssign* {
|
|
|
|
|
// If too complex, don't substitute
|
|
|
|
|
if (ops > SUBST_MAX_OPS_SUBST) return nullptr;
|
|
|
|
|
// AstCvtPackedToArray can't be anywhere else than on the RHS of assignment
|
|
|
|
|
if (VN_IS(nodep->rhsp(), CvtPackedToArray)) return nullptr;
|
|
|
|
|
// If non const but want const subtitutions only
|
|
|
|
|
if (refp->varp()->substConstOnly() && !VN_IS(nodep->rhsp(), Const)) return nullptr;
|
|
|
|
|
// Otherwise can substitute based on the assignment
|
|
|
|
|
return nodep;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// If LHS is a whole variable reference, track the whole variable
|
|
|
|
|
if (AstVarRef* const refp = VN_CAST(nodep->lhsp(), VarRef)) {
|
|
|
|
|
getEntry(refp).assignWhole(getAssignp(refp), ++m_assignStep);
|
|
|
|
|
return;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// If LHS is a known word reference, track the word
|
|
|
|
|
if (const AstWordSel* const selp = VN_CAST(nodep->lhsp(), WordSel)) {
|
|
|
|
|
if (AstVarRef* const refp = VN_CAST(selp->fromp(), VarRef)) {
|
|
|
|
|
// Simplify the index
|
|
|
|
|
simplify(selp->bitp());
|
|
|
|
|
if (const AstConst* const idxp = VN_CAST(selp->bitp(), Const)) {
|
|
|
|
|
getEntry(refp).assignWord(getAssignp(refp), ++m_assignStep, idxp->toUInt());
|
|
|
|
|
return;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
// Not tracked, iterate LHS to simplify/reset tracking
|
|
|
|
|
iterate(nodep->lhsp());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2023-11-12 17:01:07 +01:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
void visit(AstWordSel* nodep) override {
|
|
|
|
|
if (!m_funcp) return;
|
2023-11-12 17:01:07 +01:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
// Simplify the index
|
|
|
|
|
simplify(nodep->bitp());
|
2025-06-23 23:58:26 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
// If this is a known word reference, track/substitute it
|
|
|
|
|
if (AstVarRef* const refp = VN_CAST(nodep->fromp(), VarRef)) {
|
|
|
|
|
if (const AstConst* const idxp = VN_CAST(nodep->bitp(), Const)) {
|
|
|
|
|
SubstVarEntry& entry = getEntry(refp);
|
|
|
|
|
const uint32_t word = idxp->toUInt();
|
|
|
|
|
|
|
|
|
|
// If it's a write, reset tracking as we don't know the assigned value,
|
|
|
|
|
// otherwise we would have picked it up in visit(AstNodeAssign*)
|
|
|
|
|
if (refp->access().isWriteOrRW()) {
|
|
|
|
|
entry.assignWord(nullptr, ++m_assignStep, word);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 18:08:49 +01:00
|
|
|
// Otherwise it's a read,
|
2026-02-01 06:07:13 +01:00
|
|
|
UASSERT_OBJ(refp->access().isReadOnly(), nodep, "Invalid access");
|
2026-02-01 18:08:49 +01:00
|
|
|
|
|
|
|
|
// If it's a constant pool variable, substiute with the constant word
|
|
|
|
|
AstVar* const varp = refp->varp();
|
|
|
|
|
if (varp->user2()) {
|
|
|
|
|
AstConst* const constp = VN_AS(varp->valuep(), Const);
|
|
|
|
|
const uint32_t value = constp->num().edataWord(word);
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
++m_nConstWordsReinlined;
|
|
|
|
|
substitute(nodep, new AstConst{flp, AstConst::SizedEData{}, value});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Substitute other variables if possible
|
2026-02-01 06:07:13 +01:00
|
|
|
if (isSubstitutable(refp->varp())) {
|
|
|
|
|
if (AstNodeExpr* const substp = entry.substWord(word)) {
|
|
|
|
|
++m_nWordSubstituted;
|
|
|
|
|
substitute(nodep, substp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If not substituted, mark the assignment setting this word as used
|
|
|
|
|
entry.usedWord(word);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-06-23 23:58:26 +02:00
|
|
|
}
|
2023-11-12 17:01:07 +01:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
// If not a known word reference, iterate fromp to simplify/reset tracking
|
|
|
|
|
iterate(nodep->fromp());
|
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
|
|
|
}
|
2026-02-01 06:07:13 +01:00
|
|
|
|
|
|
|
|
void visit(AstVarRef* nodep) override {
|
|
|
|
|
if (!m_funcp) return;
|
|
|
|
|
|
|
|
|
|
SubstVarEntry& entry = getEntry(nodep);
|
|
|
|
|
|
|
|
|
|
// If it's a write, reset tracking as we don't know the assigned value,
|
|
|
|
|
// otherwise we would have picked it up in visit(AstNodeAssign*)
|
|
|
|
|
if (nodep->access().isWriteOrRW()) {
|
|
|
|
|
entry.assignWhole(nullptr, ++m_assignStep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise it's a read, substitute it if possible
|
|
|
|
|
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Invalid access");
|
|
|
|
|
if (isSubstitutable(nodep->varp())) {
|
|
|
|
|
if (AstNodeExpr* const substp = entry.substWhole()) {
|
|
|
|
|
// Do not substitute a compound wide expression.
|
|
|
|
|
// The whole point of adding temporaries is to eliminate them.
|
|
|
|
|
if (!nodep->isWide() || VN_IS(substp, VarRef)) {
|
|
|
|
|
++m_nWholeSubstituted;
|
|
|
|
|
substitute(nodep, substp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If not substituted, mark the assignment setting this variable as used
|
|
|
|
|
entry.usedWhole();
|
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
|
|
|
}
|
|
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
void visit(AstNodeExpr* nodep) override {
|
|
|
|
|
if (!m_funcp) return;
|
|
|
|
|
|
|
|
|
|
// Count nodes as we descend to track complexity
|
2022-11-19 21:23:37 +01:00
|
|
|
++m_ops;
|
2026-02-01 06:07:13 +01:00
|
|
|
// First iterate children, this can cache child purity
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2026-02-01 06:07:13 +01:00
|
|
|
// Do not substitute impure expressions
|
|
|
|
|
if (!nodep->isPure()) m_ops = SUBST_MAX_OPS_NA;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2026-02-01 06:07:13 +01:00
|
|
|
void visit(AstVar*) override {} // Accelerate
|
|
|
|
|
void visit(AstConst*) override {} // Accelerate
|
|
|
|
|
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2026-02-01 18:08:49 +01:00
|
|
|
explicit SubstVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~SubstVisitor() override {
|
2026-02-01 18:08:49 +01:00
|
|
|
V3Stats::addStat("Optimizations, Subst, Substituted temps", m_nSubst);
|
|
|
|
|
V3Stats::addStat("Optimizations, Subst, Constant words reinlined", m_nConstWordsReinlined);
|
|
|
|
|
V3Stats::addStat("Optimizations, Subst, Whole variable assignments deleted",
|
2026-02-01 06:07:13 +01:00
|
|
|
m_nWholeAssignDeleted);
|
2026-02-01 18:08:49 +01:00
|
|
|
V3Stats::addStat("Optimizations, Subst, Whole variables substituted", m_nWholeSubstituted);
|
|
|
|
|
V3Stats::addStat("Optimizations, Subst, Word assignments deleted", m_nWordAssignDeleted);
|
|
|
|
|
V3Stats::addStat("Optimizations, Subst, Words substituted", m_nWordSubstituted);
|
2026-02-01 06:07:13 +01:00
|
|
|
UASSERT(m_entries.empty(), "Should not visit outside functions");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Subst class functions
|
|
|
|
|
|
|
|
|
|
void V3Subst::substituteAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ SubstVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("subst", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|