Files
verilator/src/V3CCtors.cpp
T
Geza Lore 348b9b6209 Internals: Make the constant pool a regular package (#8512)
The constant pool is now an ordinary package, created with the netlist
and instantiated under $root like any other package, and most special
handling has been removed, including AstConstPool. Lookups go through
V3ConstPool, which has a singleton instance owned by V3Global. Static
methods on V3Common form the public interfce to add/find constant pool
entries.

With that, the constant pool is usable at any stage during compilation,
so enum and dimension tables created by V3Width, and enum value tables
created by V3Randomize now also live in the constant pool instead of
$unit, so identical tables are shared.

Associative array constants are handled separately from unpacked
tables, which used to be broken but unused.

Emitted constant pool variables use direct initialization, and
`constinit` with C++20 where the type allows it. This ensures we don't
change a run-time in a way that would result in unintended code size
increase.

-fno-merge-const-pool, which was introduced years ago but never prompted
a bug report is deprecated and has no effect.

This is also prep for future work.
2026-09-27 13:25:41 +01:00

292 lines
12 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Generate C language constructors and AstCReset nodes.
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3CCtors's Transformations:
// Iterates over all modules and
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
// For each variable that needs reset, add a AstCReset node.
//
// For primary inputs, add _eval_debug_assertions.
//
// This transformation honors outputSplitCFuncs.
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3CCtors.h"
#include "V3EmitCBase.h"
#include <list>
VL_DEFINE_DEBUG_FUNCTIONS;
class VCtorType final {
public:
enum en : uint8_t { MODULE, CLASS, COVERAGE };
private:
const enum en m_e;
public:
// cppcheck-suppress noExplicitConstructor
constexpr VCtorType(en _e)
: m_e{_e} {}
bool isClass() const { return m_e == CLASS; }
bool isCoverage() const { return m_e == COVERAGE; }
};
class V3CCtorsBuilder final {
AstNodeModule* const m_modp; // Current module/class
const string m_basename;
const VCtorType m_type; // What kind of constructor are we creating
std::list<AstCFunc*> m_newFunctions; // Created functions, latest is at back
int m_numStmts = 0; // Number of statements output
AstCFunc* makeNewFunc() {
const int funcNum = m_newFunctions.size();
const string funcName = m_basename + "_" + cvtToStr(funcNum);
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
funcp->isStatic(false);
funcp->isLoose(!m_type.isClass());
funcp->keepIfEmpty(true); // TODO relax
funcp->declPrivate(true);
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
if (!m_type.isCoverage()) m_modp->ctorVarReset(true);
string preventUnusedStmt;
if (m_type.isClass()) {
funcp->argTypes(EmitCUtil::symClassVar());
preventUnusedStmt = "(void)vlSymsp; // Prevent unused variable warning";
} else if (m_type.isCoverage()) {
funcp->argTypes("bool first");
preventUnusedStmt = "(void)first; // Prevent unused variable warning";
}
if (!preventUnusedStmt.empty()) {
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
}
m_modp->addStmtsp(funcp);
m_numStmts = 0;
return funcp;
}
public:
void add(AstNode* nodep) {
if (m_newFunctions.empty()) m_newFunctions.push_back(makeNewFunc());
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
m_newFunctions.push_back(makeNewFunc());
}
m_newFunctions.back()->addStmtsp(nodep);
m_numStmts += 1;
}
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
: m_modp{nodep}
, m_basename{basename}
, m_type{type} {
// Expect coverage function to always exist, so must always create at least one.
if (m_type.isCoverage()) m_newFunctions.push_back(makeNewFunc());
}
~V3CCtorsBuilder() {
if (m_newFunctions.size() == 0) {
} else if (m_newFunctions.size() == 1) {
// No split was necessary, rename the one function to the basename
m_newFunctions.front()->name(m_basename);
} else {
// Split was necessary, create root function and call all others from that
AstCFunc* const rootFuncp = makeNewFunc();
rootFuncp->name(m_basename);
for (AstCFunc* const funcp : m_newFunctions) {
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
callp->dtypeSetVoid();
if (m_type.isClass()) {
callp->argTypes("vlSymsp");
} else {
if (m_type.isCoverage()) callp->argTypes("first");
callp->selfPointer(VSelfPointerText{VSelfPointerText::This{}});
}
rootFuncp->addStmtsp(callp->makeStmt());
}
}
}
private:
VL_UNCOPYABLE(V3CCtorsBuilder);
};
//######################################################################
// Link state, as a visitor of each AstNode
class CCtorsVisitor final : public VNVisitor {
// NODE STATE
// STATE - for current visit position (use VL_RESTORER)
AstNodeModule* m_modp = nullptr; // Current module
AstCFunc* m_cfuncp = nullptr; // Current function
V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset
std::map<AstCStmt*, const AstNodeModule*> m_ctorCalls; // Calls to _ctor_var_reset
// METHODS
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) {
const auto txtAndFlp = EmitCBaseVisitorConst::scSection(modp, type);
if (txtAndFlp.first.empty()) return;
// Use an AstCStmtUser as this is from user input
AstCStmtUser* const cstmtp = new AstCStmtUser{txtAndFlp.second};
cstmtp->add(txtAndFlp.first);
cfuncp->addStmtsp(cstmtp);
}
// VISITORS
void visit(AstNodeModule* nodep) override {
if (nodep->isConstPool()) return; // Special emit rules
if (const AstClass* const classp = VN_CAST(nodep, Class)) {
// Interface class may only have pure virtuals and params which do not need cctor reset
if (classp->isInterfaceClass()) return;
}
VL_RESTORER(m_modp);
VL_RESTORER(m_varResetp);
m_modp = nodep;
V3CCtorsBuilder var_reset{nodep, "_ctor_var_reset",
VN_IS(nodep, Class) ? VCtorType::CLASS : VCtorType::MODULE};
// cppcheck-suppress danglingLifetime
m_varResetp = &var_reset;
iterateChildren(nodep);
if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode *np = nodep->stmtsp(), *nextp; np; np = nextp) {
nextp = np->nextp();
if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) {
// ... else we don't have a static VlSym to be able to coverage insert
UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
"NodeCoverDecl should be in class's package, not class itself");
configure_coverage.add(coverp->unlinkFrBack());
}
}
}
if (AstClass* const classp = VN_CAST(nodep, Class)) {
AstCFunc* const funcp = new AstCFunc{classp->fileline(), "~", nullptr, ""};
funcp->isDestructor(true);
funcp->isStatic(false);
// If can be referred to by base pointer, need virtual delete
funcp->isVirtual(classp->isExtended());
funcp->slow(false);
insertSc(funcp, classp, VSystemCSectionType::DTOR);
classp->addStmtsp(funcp);
}
}
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_varResetp);
VL_RESTORER(m_cfuncp);
m_varResetp = nullptr;
m_cfuncp = nodep;
iterateChildren(nodep);
if (nodep->name() == "new") insertSc(nodep, m_modp, VSystemCSectionType::CTOR);
}
void visit(AstCStmt* nodep) override {
if (nodep->stmtType() == VCStmtType::CTOR_VAR_RESET_CALL) {
UASSERT_OBJ(m_modp, nodep, "ctor_var_reset call not under module");
m_ctorCalls.emplace(nodep, m_modp);
}
iterateChildren(nodep);
}
void visit(AstVar* nodep) override {
if (nodep->needsCReset()) {
AstNode* const crstp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE},
new AstCReset{nodep->fileline(), nodep, true}};
if (m_varResetp) {
m_varResetp->add(crstp);
} else if (m_cfuncp) {
nodep->addNextHere(crstp);
} else {
nodep->v3fatalSrc("Var needs CReset but nowhere to place it");
}
}
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); }
~CCtorsVisitor() override {
// Remove CStmts to ctor_var_resets that are no longer needed
for (auto& itr : m_ctorCalls) {
AstCStmt* const nodep = itr.first;
const AstNodeModule* const modp = itr.second;
if (!modp->ctorVarReset()) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
}
};
//######################################################################
void V3CCtors::evalAsserts() {
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* const funcp
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
funcp->declPrivate(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->keepIfEmpty(true);
funcp->slow(false);
funcp->ifdef("VL_DEBUG");
modp->addStmtsp(funcp);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
if (const AstBasicDType* const basicp
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
const int storedWidth = basicp->widthAlignBytes() * 8;
const int lastWordWidth = varp->width() % storedWidth;
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstVarRef* const vrefp
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This{}});
AstNodeExpr* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel{
varp->fileline(), newp,
new AstConst(varp->fileline(), varp->widthWords() - 1)};
}
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd{varp->fileline(), newp,
new AstConst(varp->fileline(), AstConst::WidthedValue{},
storedWidth, value)};
AstNodeIf* const ifp = new AstIf{
varp->fileline(), newp,
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"}};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
funcp->addStmtsp(ifp);
}
}
}
}
}
}
void V3CCtors::cctorsAll() {
UINFO(2, __FUNCTION__ << ":");
evalAsserts();
{ CCtorsVisitor{v3Global.rootp()}; }
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeEitherLevel() >= 3);
}