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: Convert BLOCKTEMPs to local variables
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2021-01-01 16:29:54 +01:00
|
|
|
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LOCALIZE TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// All modules:
|
2021-06-16 18:44:06 +02:00
|
|
|
// VARSCOPE(BLOCKTEMP...
|
2019-05-19 22:13:13 +02:00
|
|
|
// if only referenced in a CFUNC, make it local to that CFUNC
|
2021-06-16 18:44:06 +02:00
|
|
|
// VARSCOPE(others
|
2019-05-19 22:13:13 +02:00
|
|
|
// if non-public, set before used, and in single CFUNC, make it local
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Localize.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2021-06-16 18:44:06 +02:00
|
|
|
// LocalizeVisitor
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-06-16 18:44:06 +02:00
|
|
|
class LocalizeVisitor final : public AstNVisitor {
|
|
|
|
|
private:
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
2021-06-16 18:44:06 +02:00
|
|
|
// AstVar::user1p() -> First AstCFunc which references the variable
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVar::user2() -> VarFlags. Flag state
|
2021-06-16 18:44:06 +02:00
|
|
|
// AstVar::user4p() -> AstVarRef that writes whole variable, if first write ref.
|
|
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
|
AstUser2InUse m_inuser2;
|
|
|
|
|
AstUser4InUse m_inuser4;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// TYPES
|
|
|
|
|
union VarFlags {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Per-variable flags
|
|
|
|
|
// Used in user()'s so initializes to all zeros
|
|
|
|
|
struct {
|
2020-04-14 04:51:35 +02:00
|
|
|
int m_notOpt : 1; // NOT optimizable
|
|
|
|
|
int m_notStd : 1; // NOT optimizable if a non-blocktemp signal
|
|
|
|
|
int m_stdFuncAsn : 1; // Found simple assignment
|
2019-05-19 22:13:13 +02:00
|
|
|
};
|
2018-02-08 01:31:21 +01:00
|
|
|
// cppcheck-suppress unusedStructMember
|
2019-05-19 22:13:13 +02:00
|
|
|
uint32_t m_flags;
|
2021-06-16 18:44:06 +02:00
|
|
|
explicit VarFlags(AstVarScope* nodep) { m_flags = nodep->user2(); }
|
|
|
|
|
void setNodeFlags(AstVarScope* nodep) { nodep->user2(m_flags); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// STATE
|
2019-10-05 13:54:14 +02:00
|
|
|
VDouble0 m_statLocVars; // Statistic tracking
|
2020-08-16 15:55:36 +02:00
|
|
|
AstCFunc* m_cfuncp = nullptr; // Current active function
|
2021-06-16 18:44:06 +02:00
|
|
|
std::vector<AstVarScope*> m_varScopeps; // List of variables to consider for localization
|
|
|
|
|
std::unordered_multimap<const AstVarScope*, AstVarRef*>
|
|
|
|
|
m_references; // VarRefs referencing the given VarScope
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2021-06-16 18:44:06 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
|
|
|
|
|
|
void clearOptimizable(AstVarScope* nodep, const char* reason) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " NoOpt " << reason << " " << nodep << endl);
|
|
|
|
|
VarFlags flags(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
flags.m_notOpt = true;
|
|
|
|
|
flags.setNodeFlags(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-06-16 18:44:06 +02:00
|
|
|
void clearStdOptimizable(AstVarScope* nodep, const char* reason) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " NoStd " << reason << " " << nodep << endl);
|
|
|
|
|
VarFlags flags(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
flags.m_notStd = true;
|
|
|
|
|
flags.setNodeFlags(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-06-16 18:44:06 +02:00
|
|
|
void moveVarScopes() {
|
|
|
|
|
for (AstVarScope* const nodep : m_varScopeps) {
|
|
|
|
|
if (nodep->varp()->valuep()) clearOptimizable(nodep, "HasInitValue");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep, "NoStdAssign");
|
2020-04-14 04:51:35 +02:00
|
|
|
VarFlags flags(nodep);
|
2020-04-13 00:57:12 +02:00
|
|
|
|
2021-06-16 18:44:06 +02:00
|
|
|
if ((nodep->varp()->varType() == AstVarType::BLOCKTEMP
|
|
|
|
|
|| !flags.m_notStd) // Temporary Or used only in block
|
2019-05-19 22:13:13 +02:00
|
|
|
&& !flags.m_notOpt // Optimizable
|
2021-06-16 18:44:06 +02:00
|
|
|
&& !nodep->varp()->isClassMember() && // Statically exists in design hierarchy
|
|
|
|
|
nodep->user1p()) // Is under a CFunc
|
|
|
|
|
{
|
|
|
|
|
UINFO(4, "Localizing " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
++m_statLocVars;
|
2021-06-16 18:44:06 +02:00
|
|
|
AstCFunc* const funcp = VN_CAST(nodep->user1p(), CFunc);
|
|
|
|
|
// Yank the Var and VarScope from it's parent and schedule them for deletion
|
|
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
if (varp->backp()) { // Might have already unlinked this via another AstVarScope
|
|
|
|
|
pushDeletep(varp->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
pushDeletep(nodep->unlinkFrBack());
|
|
|
|
|
|
|
|
|
|
// Create the new local variable.
|
|
|
|
|
const string newName
|
|
|
|
|
= nodep->scopep() == funcp->scopep()
|
|
|
|
|
? varp->name()
|
|
|
|
|
: nodep->scopep()->nameDotless() + "__DOT__" + varp->name();
|
|
|
|
|
|
|
|
|
|
AstVar* const newVarp
|
|
|
|
|
= new AstVar(varp->fileline(), varp->varType(), newName, varp);
|
|
|
|
|
newVarp->funcLocal(true);
|
|
|
|
|
|
|
|
|
|
// Fix up all the references
|
|
|
|
|
const auto er = m_references.equal_range(nodep);
|
|
|
|
|
for (auto it = er.first; it != er.second; ++it) {
|
|
|
|
|
AstVarRef* const refp = it->second;
|
|
|
|
|
refp->varScopep(nullptr);
|
|
|
|
|
refp->varp(newVarp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the var to this function, and mark local
|
|
|
|
|
funcp->addInitsp(newVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
clearOptimizable(nodep, "NotDone");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-16 18:44:06 +02:00
|
|
|
m_varScopeps.clear();
|
|
|
|
|
m_references.clear();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNetlist* nodep) override {
|
2021-06-16 18:44:06 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
moveVarScopes();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstCFunc* nodep) override {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " CFUNC " << nodep << endl);
|
2020-10-31 13:59:35 +01:00
|
|
|
VL_RESTORER(m_cfuncp);
|
|
|
|
|
{
|
|
|
|
|
m_cfuncp = nodep;
|
|
|
|
|
searchFuncStmts(nodep->argsp());
|
|
|
|
|
searchFuncStmts(nodep->initsp());
|
|
|
|
|
searchFuncStmts(nodep->stmtsp());
|
|
|
|
|
searchFuncStmts(nodep->finalsp());
|
2021-06-16 18:44:06 +02:00
|
|
|
iterateChildrenConst(nodep);
|
2020-10-31 13:59:35 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void searchFuncStmts(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Search for basic assignments to allow moving non-blocktemps
|
|
|
|
|
// For now we only find simple assignments not under any other statement.
|
|
|
|
|
// This could be more complicated; allow always-set under both branches of a IF.
|
|
|
|
|
// If so, check for ArrayRef's and such, as they aren't acceptable.
|
2020-04-14 04:51:35 +02:00
|
|
|
for (; nodep; nodep = nodep->nextp()) {
|
2021-06-16 18:44:06 +02:00
|
|
|
if (AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) {
|
|
|
|
|
if (AstVarRef* const varrefp = VN_CAST(assignp->lhsp(), VarRef)) {
|
2020-11-07 16:37:55 +01:00
|
|
|
UASSERT_OBJ(varrefp->access().isWriteOrRW(), varrefp,
|
2021-06-16 18:44:06 +02:00
|
|
|
"LHS of assignment is not an lvalue");
|
|
|
|
|
AstVarScope* const varScopep = varrefp->varScopep();
|
|
|
|
|
if (!varScopep->user4p()) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " FuncAsn " << varrefp << endl);
|
2021-06-16 18:44:06 +02:00
|
|
|
varScopep->user4p(varrefp);
|
|
|
|
|
VarFlags flags(varScopep);
|
2019-05-19 22:13:13 +02:00
|
|
|
flags.m_stdFuncAsn = true;
|
2021-06-16 18:44:06 +02:00
|
|
|
flags.setNodeFlags(varScopep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-16 18:44:06 +02:00
|
|
|
virtual void visit(AstVarScope* nodep) override {
|
|
|
|
|
if (!nodep->varp()->isPrimaryIO() // Not an IO the user wants to interact with
|
|
|
|
|
&& !nodep->varp()->isSigPublic() // Not something the user wants to interact with
|
|
|
|
|
&& !nodep->varp()->isFuncLocal() // Not already a function local (e.g.: argument)
|
|
|
|
|
&& !nodep->varp()->isStatic() // Not a static variable
|
|
|
|
|
) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " BLKVAR " << nodep << endl);
|
2021-06-16 18:44:06 +02:00
|
|
|
m_varScopeps.push_back(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// No iterate; Don't want varrefs under it
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstVarRef* nodep) override {
|
2021-06-16 18:44:06 +02:00
|
|
|
AstVarScope* const varScopep = nodep->varScopep();
|
|
|
|
|
if (!VarFlags(varScopep).m_notOpt) {
|
|
|
|
|
// Remember the reference
|
|
|
|
|
m_references.emplace(varScopep, nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_cfuncp) { // Not in function, can't optimize
|
2020-05-30 01:35:54 +02:00
|
|
|
// Perhaps impossible, but better safe
|
2021-06-16 18:44:06 +02:00
|
|
|
clearOptimizable(varScopep, "BVnofunc"); // LCOV_EXCL_LINE
|
2020-04-14 04:51:35 +02:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Allow a variable to appear in only a single function
|
2021-06-16 18:44:06 +02:00
|
|
|
AstNode* const oldfunc = varScopep->user1p();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!oldfunc) {
|
2021-06-16 18:44:06 +02:00
|
|
|
// First encounter with this variable
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " BVnewref " << nodep << endl);
|
2021-06-16 18:44:06 +02:00
|
|
|
varScopep->user1p(m_cfuncp); // Remember where it was used
|
|
|
|
|
} else if (m_cfuncp != oldfunc) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Used in multiple functions
|
2021-06-16 18:44:06 +02:00
|
|
|
clearOptimizable(varScopep, "BVmultiF");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// First varref in function must be assignment found earlier
|
2021-06-16 18:44:06 +02:00
|
|
|
const AstVarRef* const firstasn = VN_CAST(varScopep->user4p(), VarRef);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (firstasn && nodep != firstasn) {
|
2021-06-16 18:44:06 +02:00
|
|
|
clearStdOptimizable(varScopep, "notFirstAsn");
|
|
|
|
|
varScopep->user4p(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// No iterate; Don't want varrefs under it
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-06-16 18:44:06 +02:00
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit LocalizeVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2020-08-15 17:44:10 +02:00
|
|
|
virtual ~LocalizeVisitor() override {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Optimizations, Vars localized", m_statLocVars);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Localize class functions
|
|
|
|
|
|
|
|
|
|
void V3Localize::localizeAll(AstNetlist* nodep) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-06-16 18:44:06 +02:00
|
|
|
{ LocalizeVisitor visitor(nodep); } // Destruct before checking
|
2017-09-18 04:52:57 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("localize", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|