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 {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->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-08-22 22:43:49 +02:00
|
|
|
void visit(AstAssignAlias* nodep) override {
|
|
|
|
|
// 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
|
|
|
|
|
// To keep correct visual order, must add before other Text's
|
2022-09-15 20:43:56 +02:00
|
|
|
AstText* afterp = nodep->scopeAttrp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (afterp) afterp->unlinkFrBackWithNext();
|
2024-01-29 02:24:28 +01:00
|
|
|
nodep->addScopeAttrp(new AstText{nodep->fileline(), "__DOT__"s + m_cellp->name()});
|
2022-09-15 20:43:56 +02:00
|
|
|
if (afterp) nodep->addScopeAttrp(afterp);
|
2019-05-19 22:13:13 +02:00
|
|
|
afterp = nodep->scopeEntrp();
|
|
|
|
|
if (afterp) afterp->unlinkFrBackWithNext();
|
2024-01-29 02:24:28 +01:00
|
|
|
nodep->addScopeEntrp(new AstText{nodep->fileline(), "__DOT__"s + m_cellp->name()});
|
2022-09-15 20:43:56 +02:00
|
|
|
if (afterp) nodep->addScopeEntrp(afterp);
|
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
|
|
|
|
|
// alias for the inined variable, in order to resovle hierarchical refrences
|
|
|
|
|
// 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
|
|
|
|
|
// of their contaning modules is disabled so they wont reach here.
|
|
|
|
|
|
|
|
|
|
// 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)) {
|
|
|
|
|
modp->addStmtsp(new AstAssignW{flp, portRef(VAccess::WRITE), pinp->cloneTree(false)});
|
|
|
|
|
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(
|
|
|
|
|
new AstAssignVarScope{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)});
|
|
|
|
|
} else {
|
|
|
|
|
modp->addStmtsp(
|
|
|
|
|
new AstAssignAlias{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)});
|
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) {
|
|
|
|
|
modp->addStmtsp(new AstAssignW{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)});
|
|
|
|
|
} else if (nodep->direction() == VDirection::OUTPUT) {
|
|
|
|
|
modp->addStmtsp(new AstAssignW{flp, pinRef(VAccess::WRITE), portRef(VAccess::READ)});
|
|
|
|
|
} else {
|
|
|
|
|
pinExprp->v3fatalSrc("V3Tristate left INOUT port"); // LCOV_EXCL_LINE
|
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.
|
|
|
|
|
modp->addInlinesp(new AstCellInline{cellp->fileline(), cellp->name(),
|
|
|
|
|
cellp->modp()->origName(), cellp->modp()->timeunit()});
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
VL_DO_DANGLING(cellp->unlinkFrBack(), cellp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
|
|
// Consider each cell inside the current module for inling
|
|
|
|
|
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
|
|
|
|
|
// AstAssignVarScope created earlier but that'll get cleared up later
|
|
|
|
|
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
|
|
|
}
|