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: Add temporaries, such as for inline nodes
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +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-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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Inline's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each module:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Look for CELL... PRAGMA INLINE_MODULE
|
|
|
|
|
// Replicate the cell's module
|
|
|
|
|
// Convert pins to wires that make assignments
|
|
|
|
|
// Rename vars to include cell name
|
|
|
|
|
// Insert cell's module statements into the upper module
|
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 "V3Inline.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
|
|
|
|
#include "V3AstUserAllocator.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Inst.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
#include <unordered_set>
|
2021-12-22 12:41:29 +01:00
|
|
|
#include <vector>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// CONFIG
|
2019-05-19 22:13:13 +02:00
|
|
|
static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
2021-12-22 12:41:29 +01:00
|
|
|
// Inlining state. Kept as AstNodeModule::user1p via AstUser1Allocator
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2024-01-20 21:06:46 +01:00
|
|
|
struct ModuleState final {
|
2021-12-22 12:41:29 +01:00
|
|
|
bool m_inlined = false; // Whether to inline this module
|
|
|
|
|
unsigned m_cellRefs = 0; // Number of AstCells instantiating this module
|
|
|
|
|
std::vector<AstCell*> m_childCells; // AstCells under this module (to speed up traversal)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using ModuleStateUser1Allocator = AstUser1Allocator<AstNodeModule, ModuleState>;
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Visitor that determines which modules will be inlined
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class InlineMarkVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
2013-05-26 17:17:42 +02:00
|
|
|
// Output
|
2021-12-22 12:41:29 +01:00
|
|
|
// AstNodeModule::user1() // OUTPUT: ModuleState instance (via m_moduleState)
|
2017-10-02 00:00:27 +02:00
|
|
|
// Internal state (can be cleared after this visit completes)
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNodeModule::user2() // CIL_*. Allowed to automatically inline module
|
2017-10-02 00:00:27 +02:00
|
|
|
// AstNodeModule::user4() // int. Statements in module
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser2InUse m_inuser2;
|
|
|
|
|
const VNUser4InUse m_inuser4;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-12-22 12:41:29 +01:00
|
|
|
ModuleStateUser1Allocator& m_moduleState;
|
|
|
|
|
|
2017-10-02 00:00:27 +02:00
|
|
|
// For the user2 field:
|
2020-08-16 18:05:35 +02:00
|
|
|
enum : uint8_t {
|
2020-03-16 03:30:20 +01:00
|
|
|
CIL_NOTHARD = 0, // Inline not supported
|
|
|
|
|
CIL_NOTSOFT, // Don't inline unless user overrides
|
|
|
|
|
CIL_MAYBE, // Might inline
|
|
|
|
|
CIL_USER
|
|
|
|
|
}; // Pragma suggests inlining
|
2013-05-26 17:17:42 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2020-03-16 03:30:20 +01:00
|
|
|
VDouble0 m_statUnsup; // Statistic tracking
|
2021-12-21 17:21:27 +01:00
|
|
|
std::vector<AstNodeModule*> m_allMods; // All modules, in top-down order.
|
2017-10-02 00:00:27 +02:00
|
|
|
|
|
|
|
|
// Within the context of a given module, LocalInstanceMap maps
|
|
|
|
|
// from child modules to the count of each child's local instantiations.
|
2021-12-21 17:21:27 +01:00
|
|
|
using LocalInstanceMap = std::unordered_map<AstNodeModule*, unsigned>;
|
2017-10-02 00:00:27 +02:00
|
|
|
|
|
|
|
|
// We keep a LocalInstanceMap for each module in the design
|
2020-11-26 02:57:30 +01:00
|
|
|
std::unordered_map<AstNodeModule*, LocalInstanceMap> m_instances;
|
2017-10-02 00:00:27 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2013-05-26 17:17:42 +02:00
|
|
|
void cantInline(const char* reason, bool hard) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (hard) {
|
|
|
|
|
if (m_modp->user2() != CIL_NOTHARD) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " No inline hard: " << reason << " " << m_modp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->user2(CIL_NOTHARD);
|
|
|
|
|
++m_statUnsup;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (m_modp->user2() == CIL_MAYBE) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " No inline soft: " << reason << " " << m_modp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->user2(CIL_NOTSOFT);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2021-12-21 17:21:27 +01:00
|
|
|
UASSERT_OBJ(!m_modp, nodep, "Unsupported: Nested modules");
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_modp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp = nodep;
|
2017-10-02 00:00:27 +02:00
|
|
|
m_allMods.push_back(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->user2(CIL_MAYBE);
|
|
|
|
|
m_modp->user4(0); // statement count
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(m_modp, Iface)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Inlining an interface means we no longer have a cell handle to resolve to.
|
|
|
|
|
// If inlining moves post-scope this can perhaps be relaxed.
|
|
|
|
|
cantInline("modIface", true);
|
|
|
|
|
}
|
2021-01-18 14:03:18 +01:00
|
|
|
if (m_modp->modPublic() && (m_modp->isTop() || !v3Global.opt.flatten())) {
|
|
|
|
|
cantInline("modPublic", false);
|
|
|
|
|
}
|
2017-10-02 00:00:27 +02:00
|
|
|
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2020-04-05 15:30:23 +02:00
|
|
|
// TODO allow inlining of modules that have classes
|
|
|
|
|
// (Probably wait for new inliner scheme)
|
|
|
|
|
cantInline("class", true);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2021-12-22 12:41:29 +01:00
|
|
|
m_moduleState(nodep->modp()).m_cellRefs++;
|
|
|
|
|
m_moduleState(m_modp).m_childCells.push_back(nodep);
|
2017-10-02 00:00:27 +02:00
|
|
|
m_instances[m_modp][nodep->modp()]++;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPragma* nodep) override {
|
2022-01-02 19:56:40 +01:00
|
|
|
if (nodep->pragType() == VPragmaType::INLINE_MODULE) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_modp) {
|
2019-07-01 04:37:03 +02:00
|
|
|
nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_modp->user2() == CIL_MAYBE || m_modp->user2() == CIL_NOTSOFT) {
|
2017-10-02 00:00:27 +02:00
|
|
|
m_modp->user2(CIL_USER);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-12-21 17:21:27 +01:00
|
|
|
// Remove so it does not propagate to upper cell
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->pragType() == VPragmaType::NO_INLINE_MODULE) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_modp) {
|
2019-07-01 04:37:03 +02:00
|
|
|
nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE
|
2021-01-18 14:03:18 +01:00
|
|
|
} else if (!v3Global.opt.flatten()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
cantInline("Pragma NO_INLINE_MODULE", false);
|
|
|
|
|
}
|
2021-12-21 17:21:27 +01:00
|
|
|
// Remove so it does not propagate to upper cell
|
2020-01-17 02:17:11 +01:00
|
|
|
// Remove so don't propagate to upper cell...
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarXRef* nodep) override {
|
2021-12-21 17:21:27 +01:00
|
|
|
// Remove link. V3LinkDot will reestablish it after inlining.
|
2020-08-15 16:12:55 +02:00
|
|
|
nodep->varp(nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2021-12-21 17:21:27 +01:00
|
|
|
// Remove link. V3LinkDot will reestablish it after inlining.
|
2020-03-07 18:52:11 +01:00
|
|
|
// MethodCalls not currently supported by inliner, so keep linked
|
2023-09-18 15:21:30 +02:00
|
|
|
if (!nodep->classOrPackagep() && !VN_IS(nodep, MethodCall)) {
|
|
|
|
|
nodep->taskp(nullptr);
|
2023-10-15 12:25:42 +02:00
|
|
|
VIsCached::clearCacheTree();
|
2023-09-18 15:21:30 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlways* nodep) override {
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
if (nodep->keyword() != VAlwaysKwd::CONT_ASSIGN) nodep->user4Inc(); // statement count
|
2021-12-21 17:21:27 +01:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't count assignments, as they'll likely flatten out
|
|
|
|
|
// Still need to iterate though to nullify VarXRefs
|
2021-06-21 00:32:57 +02:00
|
|
|
const int oldcnt = m_modp->user4();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2017-10-02 00:00:27 +02:00
|
|
|
m_modp->user4(oldcnt);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNetlist* nodep) override {
|
2021-12-22 12:41:29 +01:00
|
|
|
// Build ModuleState, user2, and user4 for all modules.
|
2017-10-02 00:00:27 +02:00
|
|
|
// Also build m_allMods and m_instances.
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2017-10-02 00:00:27 +02:00
|
|
|
|
|
|
|
|
// Iterate through all modules in bottom-up order.
|
|
|
|
|
// Make a final inlining decision for each.
|
2022-04-18 19:03:56 +02:00
|
|
|
for (AstNodeModule* const modp : vlstd::reverse_view(m_allMods)) {
|
2017-10-02 00:00:27 +02:00
|
|
|
|
|
|
|
|
// If we're going to inline some modules into this one,
|
|
|
|
|
// update user4 (statement count) to reflect that:
|
|
|
|
|
int statements = modp->user4();
|
2021-12-21 17:21:27 +01:00
|
|
|
for (const auto& pair : m_instances[modp]) {
|
|
|
|
|
const AstNodeModule* const childp = pair.first;
|
2021-12-22 12:41:29 +01:00
|
|
|
if (m_moduleState(childp).m_inlined) { // inlining child
|
2021-12-21 17:21:27 +01:00
|
|
|
statements += childp->user4() * pair.second;
|
2017-10-02 00:00:27 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
modp->user4(statements);
|
|
|
|
|
|
2021-06-21 00:32:57 +02:00
|
|
|
const int allowed = modp->user2();
|
2021-12-22 12:41:29 +01:00
|
|
|
const int refs = m_moduleState(modp).m_cellRefs;
|
2017-10-02 00:00:27 +02:00
|
|
|
|
|
|
|
|
// Should we automatically inline this module?
|
2020-04-22 00:14:08 +02:00
|
|
|
// If --flatten is specified, then force everything to be inlined that can be.
|
2017-10-02 00:00:27 +02:00
|
|
|
// inlineMult = 2000 by default.
|
|
|
|
|
// If a mod*#refs is < this # nodes, can inline it
|
|
|
|
|
// Packages aren't really "under" anything so they confuse this algorithm
|
2021-12-21 17:21:27 +01:00
|
|
|
const bool doit = !VN_IS(modp, Package) //
|
|
|
|
|
&& allowed != CIL_NOTHARD //
|
|
|
|
|
&& allowed != CIL_NOTSOFT //
|
|
|
|
|
&& (allowed == CIL_USER //
|
|
|
|
|
|| v3Global.opt.flatten() //
|
|
|
|
|
|| refs == 1 //
|
|
|
|
|
|| statements < INLINE_MODS_SMALLER //
|
|
|
|
|
|| v3Global.opt.inlineMult() < 1 //
|
|
|
|
|
|| refs * statements < v3Global.opt.inlineMult());
|
2021-12-22 12:41:29 +01:00
|
|
|
m_moduleState(modp).m_inlined = doit;
|
2020-03-16 03:30:20 +01:00
|
|
|
UINFO(4, " Inline=" << doit << " Possible=" << allowed << " Refs=" << refs
|
2025-05-23 02:29:32 +02:00
|
|
|
<< " Stmts=" << statements << " " << modp);
|
2017-10-02 00:00:27 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2021-12-21 17:21:27 +01:00
|
|
|
if (m_modp) m_modp->user4Inc(); // Inc statement count
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2021-12-22 12:41:29 +01:00
|
|
|
explicit InlineMarkVisitor(AstNode* nodep, ModuleStateUser1Allocator& moduleState)
|
|
|
|
|
: m_moduleState{moduleState} {
|
|
|
|
|
iterate(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~InlineMarkVisitor() override {
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-05-25 16:42:44 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// After cell is cloned, relink the new module's contents
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class InlineRelinkVisitor final : public VNVisitor {
|
2013-05-25 16:42:44 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Input:
|
|
|
|
|
// See InlineVisitor
|
|
|
|
|
|
|
|
|
|
// STATE
|
2021-03-12 23:26:53 +01:00
|
|
|
std::unordered_set<std::string> m_renamedInterfaces; // Name of renamed interface variables
|
2025-08-22 22:43:49 +02:00
|
|
|
AstNodeModule* const m_modp; // The module we are inlining into
|
|
|
|
|
const AstCell* const m_cellp; // The cell being inlined
|
|
|
|
|
size_t m_nPlaceholders = 0; // Unique identifier sequence number for placeholder variables
|
2013-05-25 16:42:44 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCellInline* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Inlined cell under the inline cell, need to move to avoid conflicts
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
m_modp->addInlinesp(nodep);
|
|
|
|
|
// Rename
|
2021-12-21 17:21:27 +01:00
|
|
|
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(6, " Inline " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Do CellInlines under this, but don't move them
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Cell under the inline cell, need to rename to avoid conflicts
|
2021-12-21 17:21:27 +01:00
|
|
|
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2021-12-21 17:21:27 +01:00
|
|
|
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
2020-04-05 15:30:23 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstModule* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_renamedInterfaces.clear();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2016-01-22 01:11:53 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_renamedInterfaces.insert(nodep->name());
|
|
|
|
|
// Each inlined cell that contain an interface variable need to
|
|
|
|
|
// copy the IfaceRefDType and point it to the newly cloned
|
|
|
|
|
// interface cell.
|
2021-11-13 19:50:44 +01:00
|
|
|
AstIfaceRefDType* const newdp = ifacerefp->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->dtypep(newdp);
|
|
|
|
|
ifacerefp->addNextHere(newdp);
|
|
|
|
|
// Relink to point to newly cloned cell
|
|
|
|
|
if (newdp->cellp()) {
|
2025-08-22 22:43:49 +02:00
|
|
|
if (AstCell* const newcellp = VN_CAST(newdp->cellp()->user3p(), Cell)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
newdp->cellp(newcellp);
|
|
|
|
|
newdp->cellName(newcellp->name());
|
|
|
|
|
// Tag the old ifacerefp to ensure it leaves no stale
|
|
|
|
|
// reference to the inlined cell.
|
2023-10-29 02:12:27 +02:00
|
|
|
newdp->user1(false);
|
|
|
|
|
ifacerefp->user1(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Variable under the inline cell, need to rename to avoid conflicts
|
|
|
|
|
// Also clear I/O bits, as it is now local.
|
2021-06-21 00:32:57 +02:00
|
|
|
const string name = m_cellp->name() + "__DOT__" + nodep->name();
|
2020-04-05 15:30:23 +02:00
|
|
|
if (!nodep->isFuncLocal() && !nodep->isClassMember()) nodep->inlineAttrReset(name);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_cellp->isTrace()) nodep->trace(false);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "varchanged");
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Function under the inline cell, need to rename to avoid conflicts
|
|
|
|
|
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTypedef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Typedef under the inline cell, need to rename to avoid conflicts
|
|
|
|
|
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2025-09-22 22:30:26 +02:00
|
|
|
void visit(AstAlias* nodep) override {
|
2025-08-22 22:43:49 +02:00
|
|
|
// Don't replace port variable in the alias
|
2025-04-09 02:48:57 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2025-08-22 22:43:49 +02:00
|
|
|
// If the target port is being inlined, replace reference with the
|
|
|
|
|
// connected expression (always a Const of a VarRef).
|
|
|
|
|
AstNode* const pinExpr = nodep->varp()->user2p();
|
|
|
|
|
if (!pinExpr) return;
|
|
|
|
|
|
|
|
|
|
// If it's a constant, inline it
|
|
|
|
|
if (AstConst* const constp = VN_CAST(pinExpr, Const)) {
|
|
|
|
|
// You might think we would not try to substitute a constant for
|
|
|
|
|
// a written variable, but we might need to do this if for example
|
|
|
|
|
// there is an assignment to an input port, and that input port
|
|
|
|
|
// is tied to a constant on the cell we are inlining. This does
|
|
|
|
|
// generate an ASSIGNIN warning, but that can be downgraded to
|
|
|
|
|
// a warning. (Also assigning to an input can has valid uses if
|
|
|
|
|
// e.g. done via a hierarchical reference from outside to an input
|
|
|
|
|
// unconnected on the instance, so we don't want ASSIGNIN fatal.)
|
|
|
|
|
// Same applies when there is a static initialzier for an input.
|
|
|
|
|
// To avoid having to special case malformed assignment, or worse
|
|
|
|
|
// yet emiting code like 0 = 0, we instead substitute a placeholder
|
|
|
|
|
// variable that will later be pruned (it will otherwise be unreferenced).
|
|
|
|
|
if (!nodep->access().isReadOnly()) {
|
|
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
const std::string name = "__vInlPlaceholder_" + std::to_string(++m_nPlaceholders);
|
|
|
|
|
AstVar* const holdep = new AstVar{varp->fileline(), VVarType::VAR, name, varp};
|
|
|
|
|
m_modp->addStmtsp(holdep);
|
|
|
|
|
AstVarRef* const newp = new AstVarRef{nodep->fileline(), holdep, nodep->access()};
|
|
|
|
|
nodep->replaceWith(newp);
|
2020-03-16 03:30:20 +01:00
|
|
|
} else {
|
2025-08-22 22:43:49 +02:00
|
|
|
nodep->replaceWith(constp->cloneTree(false));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-08-22 22:43:49 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-08-22 22:43:49 +02:00
|
|
|
|
|
|
|
|
// Otherwise it must be a variable reference, retarget this ref
|
|
|
|
|
const AstVarRef* const vrefp = VN_AS(pinExpr, VarRef);
|
|
|
|
|
nodep->varp(vrefp->varp());
|
|
|
|
|
nodep->classOrPackagep(vrefp->classOrPackagep());
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarXRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Track what scope it was originally under so V3LinkDot can resolve it
|
2021-12-21 17:21:27 +01:00
|
|
|
nodep->inlinedDots(VString::dot(m_cellp->name(), ".", nodep->inlinedDots()));
|
2020-11-11 03:40:14 +01:00
|
|
|
for (string tryname = nodep->dotted(); true;) {
|
2017-12-14 01:42:49 +01:00
|
|
|
if (m_renamedInterfaces.count(tryname)) {
|
|
|
|
|
nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// If foo.bar, and foo is an interface, then need to search again for foo
|
2021-11-26 23:55:36 +01:00
|
|
|
const string::size_type pos = tryname.rfind('.');
|
2020-03-16 03:30:20 +01:00
|
|
|
if (pos == string::npos || pos == 0) {
|
2017-12-14 01:42:49 +01:00
|
|
|
break;
|
|
|
|
|
} else {
|
2025-08-21 10:43:37 +02:00
|
|
|
tryname.resize(pos);
|
2017-12-14 01:42:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Track what scope it was originally under so V3LinkDot can resolve it
|
2021-12-21 17:21:27 +01:00
|
|
|
nodep->inlinedDots(VString::dot(m_cellp->name(), ".", nodep->inlinedDots()));
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_renamedInterfaces.count(nodep->dotted())) {
|
|
|
|
|
nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted());
|
|
|
|
|
}
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " " << nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not needed, as V3LinkDot doesn't care about typedefs
|
2022-09-16 12:22:11 +02:00
|
|
|
// void visit(AstRefDType* nodep) override {}
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScopeName* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// If there's a %m in the display text, we add a special node that will contain the name()
|
|
|
|
|
// Similar code in V3Begin
|
2025-10-14 13:08:35 +02:00
|
|
|
// To keep correct visual order, must add before exising
|
|
|
|
|
nodep->scopeAttr("__DOT__" + m_cellp->name() + nodep->scopeAttr());
|
|
|
|
|
nodep->scopeEntr("__DOT__" + m_cellp->name() + nodep->scopeEntr());
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2025-08-04 14:29:56 +02:00
|
|
|
void visit(AstNodeCoverDecl* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Fix path in coverage statements
|
2018-03-16 00:46:05 +01:00
|
|
|
nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier()));
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2013-05-25 16:42:44 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_modp{oldModp}
|
|
|
|
|
, m_cellp{cellp} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(cloneModp);
|
2013-05-25 16:42:44 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~InlineRelinkVisitor() override = default;
|
2013-05-25 16:42:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2025-08-22 22:43:49 +02:00
|
|
|
// Module inliner
|
|
|
|
|
|
|
|
|
|
namespace ModuleInliner {
|
|
|
|
|
|
|
|
|
|
// A port variable in an inlined module can be connected 2 ways.
|
|
|
|
|
// Either add a continuous assignment between the pin expression from
|
|
|
|
|
// the instance and the port variable, or simply inline the pin expression
|
|
|
|
|
// in place of the port variable. We will prefer to do the later whenever
|
|
|
|
|
// possible (and sometimes required). When inlining, we need to create an
|
2025-08-26 00:47:08 +02:00
|
|
|
// alias for the inlined variable, in order to resovle hierarchical references
|
2025-08-22 22:43:49 +02:00
|
|
|
// against it later in V3Scope (and also for tracing, which is inserted
|
|
|
|
|
//later). Returns ture iff the given port variable should be inlined,
|
|
|
|
|
// and false if a continuous assignment should be used.
|
|
|
|
|
bool inlinePort(AstVar* nodep) {
|
|
|
|
|
// Interface references are always inlined
|
|
|
|
|
if (nodep->isIfaceRef()) return true;
|
|
|
|
|
// Ref ports must be always inlined
|
|
|
|
|
if (nodep->direction() == VDirection::REF) return true;
|
|
|
|
|
// Forced signals must not be inlined. The port signal can be
|
|
|
|
|
// forced separately from the connected signals.
|
|
|
|
|
if (nodep->isForced()) return false;
|
|
|
|
|
|
|
|
|
|
// Note: For singls marked 'public' (and not 'public_flat') inlining
|
2025-08-26 00:47:08 +02:00
|
|
|
// of their containing modules is disabled so they wont reach here.
|
2025-08-22 22:43:49 +02:00
|
|
|
|
|
|
|
|
// TODO: For now, writable public signals inside the cell cannot be
|
|
|
|
|
// eliminated as they are entered into the VerilatedScope, and
|
|
|
|
|
// changes would not propagate to it when assigned. (The alias created
|
|
|
|
|
// for them ensures they would be read correctly, but would not
|
|
|
|
|
// propagate any changes.) This can be removed when the VerialtedScope
|
|
|
|
|
// construction in V3EmitCSyms understands aliases.
|
|
|
|
|
if (nodep->isSigUserRWPublic()) return false;
|
|
|
|
|
|
|
|
|
|
// Otherwise we can repalce the variable
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Connect the given port 'nodep' (being inlined into 'modp') to the given
|
|
|
|
|
// expression (from the Cell Pin)
|
|
|
|
|
void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) {
|
|
|
|
|
UINFO(6, "Connecting " << pinExprp);
|
|
|
|
|
UINFO(6, " to " << nodep);
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Decide whether to inline the port variable or use continuous assignments
|
|
|
|
|
const bool inlineIt = inlinePort(nodep);
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// If we deccided to inline it, record the expression to substitute this variable with
|
|
|
|
|
if (inlineIt) nodep->user2p(pinExprp);
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
FileLine* const flp = nodep->fileline();
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Helper to creates an AstVarRef reference to the port variable
|
|
|
|
|
const auto portRef = [&](VAccess access) { return new AstVarRef{flp, nodep, access}; };
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// If the connected expression is a constant, add an assignment to set
|
|
|
|
|
// the port variable. The constant can still be inlined, in which case
|
|
|
|
|
// this is needed for tracing the inlined port variable.
|
|
|
|
|
if (AstConst* const pinp = VN_CAST(pinExprp, Const)) {
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const ap
|
|
|
|
|
= new AstAssignW{flp, portRef(VAccess::WRITE), pinp->cloneTree(false)};
|
|
|
|
|
modp->addStmtsp(new AstAlways{ap});
|
2025-08-22 22:43:49 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Otherwise it must be a variable reference due to having called pinReconnectSimple
|
|
|
|
|
const AstVarRef* const pinRefp = VN_AS(pinExprp, VarRef);
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Helper to create an AstVarRef reference to the pin variable
|
|
|
|
|
const auto pinRef = [&](VAccess access) {
|
|
|
|
|
AstVarRef* const p = new AstVarRef{pinRefp->fileline(), pinRefp->varp(), access};
|
|
|
|
|
p->classOrPackagep(pinRefp->classOrPackagep());
|
|
|
|
|
return p;
|
|
|
|
|
};
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// If it is being inlined, create the alias for it
|
|
|
|
|
if (inlineIt) {
|
|
|
|
|
UINFO(6, "Inlning port variable: " << nodep);
|
|
|
|
|
if (nodep->isIfaceRef()) {
|
|
|
|
|
modp->addStmtsp(
|
2025-09-30 07:40:17 +02:00
|
|
|
new AstAliasScope{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)});
|
2025-08-22 22:43:49 +02:00
|
|
|
} else {
|
2025-09-29 19:23:51 +02:00
|
|
|
AstVarRef* const aliasArgsp = portRef(VAccess::WRITE);
|
|
|
|
|
aliasArgsp->addNext(pinRef(VAccess::READ));
|
|
|
|
|
modp->addStmtsp(new AstAlias{flp, aliasArgsp});
|
2021-12-22 12:41:29 +01:00
|
|
|
}
|
2025-08-22 22:43:49 +02:00
|
|
|
// They will become the same variable, so propagate file-line and variable attributes
|
|
|
|
|
pinRefp->varp()->fileline()->modifyStateInherit(flp);
|
|
|
|
|
flp->modifyStateInherit(pinRefp->varp()->fileline());
|
|
|
|
|
pinRefp->varp()->propagateAttrFrom(nodep);
|
|
|
|
|
nodep->propagateAttrFrom(pinRefp->varp());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise create the continuous assignment between the port var and the pin expression
|
|
|
|
|
UINFO(6, "Not inlning port variable: " << nodep);
|
|
|
|
|
if (nodep->direction() == VDirection::INPUT) {
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const ap = new AstAssignW{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)};
|
|
|
|
|
modp->addStmtsp(new AstAlways{ap});
|
2025-08-22 22:43:49 +02:00
|
|
|
} else if (nodep->direction() == VDirection::OUTPUT) {
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const ap = new AstAssignW{flp, pinRef(VAccess::WRITE), portRef(VAccess::READ)};
|
|
|
|
|
modp->addStmtsp(new AstAlways{ap});
|
2025-08-22 22:43:49 +02:00
|
|
|
} else {
|
2025-10-03 12:49:13 +02:00
|
|
|
pinExprp->v3fatalSrc("V3Tristate left INOUT port");
|
2021-12-22 12:41:29 +01:00
|
|
|
}
|
2025-08-22 22:43:49 +02:00
|
|
|
}
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Inline 'cellp' into 'modp'. 'last' indicatest this is tha last instance of the inlined module
|
|
|
|
|
void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last) {
|
|
|
|
|
UINFO(5, " Inline Cell " << cellp);
|
|
|
|
|
UINFO(5, " into Module " << modp);
|
|
|
|
|
|
|
|
|
|
const VNUser2InUse user2InUse;
|
|
|
|
|
|
|
|
|
|
// Important: If this is the last cell, then don't clone the instantiated module but
|
|
|
|
|
// inline the original directly. While this requires some special casing, doing so
|
|
|
|
|
// saves us having to temporarily clone the module for the last cell, which
|
|
|
|
|
// significantly reduces Verilator memory usage. This is especially true as often the
|
|
|
|
|
// top few levels of the hierarchy are singleton wrapper modules, which we always
|
|
|
|
|
// inline. In this case this special casing saves us from having to clone essentially
|
|
|
|
|
// the entire netlist, which would in effect double Verilator memory consumption, or
|
|
|
|
|
// worse if we put off deleting the inlined modules until the end. Not having to clone
|
|
|
|
|
// large trees also improves speed.
|
|
|
|
|
|
|
|
|
|
// The module we will yank the contents out of and put into 'modp'
|
|
|
|
|
AstNodeModule* const inlinedp = last ? cellp->modp()->unlinkFrBack() //
|
|
|
|
|
: cellp->modp()->cloneTree(false);
|
|
|
|
|
|
|
|
|
|
// Compute map from original port variables and cells to their clones
|
|
|
|
|
std::unordered_map<const AstVar*, AstVar*> modVar2Clone;
|
|
|
|
|
for (AstNode *ap = cellp->modp()->stmtsp(), *bp = inlinedp->stmtsp(); ap || bp;
|
|
|
|
|
ap = ap->nextp(), bp = bp->nextp()) {
|
|
|
|
|
UASSERT_OBJ(ap && bp, ap ? ap : bp, "Clone has different number of children");
|
|
|
|
|
// We only care about AstVar and AstCell, but faster to just set them all
|
|
|
|
|
ap->user3p(bp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create data for resolving hierarchical references later.
|
2025-10-03 12:49:13 +02:00
|
|
|
modp->addInlinesp(
|
|
|
|
|
new AstCellInline{cellp->fileline(), cellp->name(), cellp->modp()->origName()});
|
2025-08-22 22:43:49 +02:00
|
|
|
|
|
|
|
|
// Connect the pins on the instance
|
|
|
|
|
for (AstPin* pinp = cellp->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
|
|
|
|
if (!pinp->exprp()) continue;
|
|
|
|
|
UINFO(6, "Conecting port " << pinp->modVarp());
|
|
|
|
|
UINFO(6, " of instance " << cellp);
|
|
|
|
|
|
|
|
|
|
// Make sure the conneccted pin expression is always a VarRef or a Const
|
|
|
|
|
V3Inst::pinReconnectSimple(pinp, cellp, false);
|
|
|
|
|
|
|
|
|
|
// Warn
|
|
|
|
|
V3Inst::checkOutputShort(pinp);
|
|
|
|
|
|
|
|
|
|
// Pick up the old and new port variables signal (new is the same on last instance)
|
|
|
|
|
const AstVar* const oldModVarp = pinp->modVarp();
|
|
|
|
|
AstVar* const newModVarp = VN_AS(oldModVarp->user3p(), Var);
|
|
|
|
|
// Pick up the connected expression (a VarRef or Const due to pinReconnectSimple)
|
|
|
|
|
AstNodeExpr* const pinExprp = VN_AS(pinp->exprp(), NodeExpr);
|
|
|
|
|
|
|
|
|
|
// Connect up the port
|
|
|
|
|
connectPort(modp, newModVarp, pinExprp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cleanup var names, etc, to not conflict, relink replaced variables
|
|
|
|
|
{ InlineRelinkVisitor{inlinedp, modp, cellp}; }
|
|
|
|
|
// Move statements from the inlined module into the module we are inlining into
|
|
|
|
|
if (AstNode* const stmtsp = inlinedp->stmtsp()) {
|
|
|
|
|
modp->addStmtsp(stmtsp->unlinkFrBackWithNext());
|
|
|
|
|
}
|
|
|
|
|
// Delete the empty shell of the inlined module
|
|
|
|
|
VL_DO_DANGLING(inlinedp->deleteTree(), inlinedp);
|
|
|
|
|
// Remove the cell we just inlined
|
2025-09-09 23:39:44 +02:00
|
|
|
VL_DO_DANGLING(cellp->unlinkFrBack()->deleteTree(), cellp);
|
2025-08-22 22:43:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply all inlining decisions
|
|
|
|
|
void process(AstNetlist* netlistp, ModuleStateUser1Allocator& moduleStates) {
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Input:
|
|
|
|
|
// AstNodeModule::user1p() // ModuleState instance (via moduleState)
|
|
|
|
|
//
|
|
|
|
|
// Cleared entire netlist
|
|
|
|
|
// AstIfaceRefDType::user1() // Whether the cell pointed to by this
|
|
|
|
|
// // AstIfaceRefDType has been inlined
|
|
|
|
|
// AstCell::user3p() // AstCell*, the clone
|
|
|
|
|
// AstVar::user3p() // AstVar*, the clone clone
|
|
|
|
|
// Cleared each cell
|
|
|
|
|
// AstVar::user2p() // AstVarRef*/AstConst* This port is connected to (AstPin::expr())
|
|
|
|
|
const VNUser3InUse m_user3InUse;
|
|
|
|
|
|
|
|
|
|
// Number of inlined instances, for statistics
|
|
|
|
|
VDouble0 m_nInlined;
|
|
|
|
|
|
|
|
|
|
// We want to inline bottom up. The modules under the netlist are in
|
|
|
|
|
// dependency order (top first, leaves last), so find the end of the list.
|
|
|
|
|
AstNode* nodep = netlistp->modulesp();
|
|
|
|
|
while (nodep->nextp()) nodep = nodep->nextp();
|
|
|
|
|
|
|
|
|
|
// Iterate module list backwards (stop when we get back to the Netlist)
|
|
|
|
|
while (AstNodeModule* const modp = VN_CAST(nodep, NodeModule)) {
|
|
|
|
|
nodep = nodep->backp();
|
|
|
|
|
|
2025-08-26 00:47:08 +02:00
|
|
|
// Consider each cell inside the current module for inlining
|
2025-08-22 22:43:49 +02:00
|
|
|
for (AstCell* const cellp : moduleStates(modp).m_childCells) {
|
|
|
|
|
ModuleState& childState = moduleStates(cellp->modp());
|
|
|
|
|
if (!childState.m_inlined) continue;
|
|
|
|
|
++m_nInlined;
|
|
|
|
|
inlineCell(modp, cellp, --childState.m_cellRefs == 0);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2016-01-22 01:11:53 +01:00
|
|
|
}
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
V3Stats::addStat("Optimizations, Inlined instances", m_nInlined);
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Clean up AstIfaceRefDType references
|
|
|
|
|
// If the cell has been removed let's make sure we don't leave a
|
|
|
|
|
// reference to it. This dtype may still be in use by the
|
2025-09-30 07:40:17 +02:00
|
|
|
// AstAliasScope created earlier but that'll get cleared up later
|
2025-08-22 22:43:49 +02:00
|
|
|
netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) {
|
|
|
|
|
if (nodep->user1()) nodep->cellp(nullptr);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} //namespace ModuleInliner
|
2013-05-25 16:42:44 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2025-08-22 22:43:49 +02:00
|
|
|
// V3Inline class functions
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
void V3Inline::inlineAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-12-22 12:41:29 +01:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
const VNUser1InUse m_inuser1; // output of InlineMarkVisitor, input to InlineVisitor.
|
|
|
|
|
ModuleStateUser1Allocator moduleState; // AstUser1Allocator
|
|
|
|
|
|
|
|
|
|
// Scoped to clean up temp userN's
|
|
|
|
|
{ InlineMarkVisitor{nodep, moduleState}; }
|
|
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
// Inline the modles we decided to inline
|
|
|
|
|
ModuleInliner::process(nodep, moduleState);
|
2021-12-22 12:41:29 +01:00
|
|
|
|
|
|
|
|
// Check inlined modules have been removed during traversal. Otherwise we might have blown
|
|
|
|
|
// up Verilator memory consumption.
|
|
|
|
|
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
|
|
|
|
|
modp = VN_AS(modp->nextp(), NodeModule)) {
|
|
|
|
|
UASSERT_OBJ(!moduleState(modp).m_inlined, modp,
|
2025-05-03 10:00:47 +02:00
|
|
|
"Inlined module should have been deleted when the last instance "
|
|
|
|
|
"referencing it was inlined");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-12-22 12:41:29 +01:00
|
|
|
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("inline", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|