verilator/src/V3Class.cpp

293 lines
12 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Handle SV classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
2025-01-01 14:30:25 +01:00
// Copyright 2003-2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Class's Transformations:
//
// Each class:
// Move to be modules under AstNetlist
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Class.h"
2023-06-07 00:28:35 +02:00
#include "V3UniqueNames.h"
#include <queue>
#include <set>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class ClassVisitor final : public VNVisitor {
// NODE STATE
// AstClass::user1() -> bool. True if iterated already
// AstVar::user1p() -> AstVarScope* Scope used with this var
const VNUser1InUse m_inuser1;
// MEMBERS
string m_prefix; // String prefix to add to class name based on hier
2023-06-07 00:28:35 +02:00
V3UniqueNames m_names; // For unique naming of structs and unions
2022-12-21 01:22:42 +01:00
AstNodeModule* m_modp = nullptr; // Current module
2022-01-02 21:09:07 +01:00
AstNodeModule* m_classPackagep = nullptr; // Package moving into
const AstScope* m_classScopep = nullptr; // Package moving scopes into
AstScope* m_packageScopep = nullptr; // Class package scope
const AstNodeFTask* m_ftaskp = nullptr; // Current task
std::vector<std::pair<AstNode*, AstScope*>> m_toScopeMoves;
2022-01-02 21:09:07 +01:00
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
std::set<AstTypedef*> m_typedefps; // Contains all typedef nodes
std::set<AstNodeUOrStructDType*> m_strDtypeps; // Contains all packed structs and unions
// Contains all public packed structs and unions, using a queue to
// mark embedded struct / union public by BFS
std::queue<AstNodeUOrStructDType*> m_pubStrDtypeps;
// METHODS
void recurseImplements(AstClass* nodep) {
// In SystemVerilog, we have two inheritance chains:
// - extends of concrete clasess: mapped to non-virtual C++ inheritance
// as there is only single ancestor allowed
// - implements of concrete classes / extends of interface classes: mapped
// to virtual inheritance to allow diamond patterns with multiple ancestors
2025-07-11 19:11:31 +02:00
if (nodep->useVirtualPublic()) return; // Short-circuit to exit diamond cycles
if (nodep->isInterfaceClass()) { nodep->useVirtualPublic(true); }
for (const AstClassExtends* extp = nodep->extendsp(); extp;
2025-07-11 19:11:31 +02:00
extp = VN_AS(extp->nextp(), ClassExtends)) {
recurseImplements(extp->classp());
}
}
// VISITORS
void visit(AstClass* nodep) override {
if (nodep->user1SetOnce()) return;
// Move this class
nodep->name(m_prefix + nodep->name());
nodep->unlinkFrBack();
v3Global.rootp()->addModulesp(nodep);
recurseImplements(nodep);
// Make containing package
// Note origName is the same as the class origName so errors look correct
AstClassPackage* const packagep
= new AstClassPackage{nodep->fileline(), nodep->origName(), nodep->libname()};
packagep->name(nodep->name() + "__Vclpkg");
nodep->editCountInc();
nodep->classOrPackagep(packagep);
packagep->classp(nodep);
packagep->timeunit(nodep->timeunit());
v3Global.rootp()->addModulesp(packagep);
// Add package to hierarchy
2022-01-02 15:46:15 +01:00
AstCell* const cellp = new AstCell{packagep->fileline(),
packagep->fileline(),
packagep->name(),
packagep->name(),
nullptr,
nullptr,
nullptr};
cellp->modp(packagep);
v3Global.rootp()->topModulep()->addStmtsp(cellp);
// Find class's scope
// Alternative would be to move this and related to V3Scope
const AstScope* classScopep = nullptr;
for (AstNode* itp = nodep->stmtsp(); itp; itp = itp->nextp()) {
if ((classScopep = VN_CAST(itp, Scope))) break;
}
UASSERT_OBJ(classScopep, nodep, "No scope under class");
// Add scope
AstScope* const scopep
= new AstScope{nodep->fileline(), packagep, classScopep->name(),
classScopep->aboveScopep(), classScopep->aboveCellp()};
packagep->addStmtsp(scopep);
// Iterate
VL_RESTORER(m_prefix);
2022-01-02 21:09:07 +01:00
VL_RESTORER(m_classPackagep);
VL_RESTORER(m_classScopep);
VL_RESTORER(m_packageScopep);
2022-12-21 01:22:42 +01:00
VL_RESTORER(m_modp);
m_modp = nodep;
m_classPackagep = packagep;
m_classScopep = classScopep;
m_packageScopep = scopep;
m_prefix = nodep->name() + "__02e"; // .
iterateChildren(nodep);
}
void visit(AstNodeModule* nodep) override {
2020-11-27 15:42:51 +01:00
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
// Classes are always under a Package (perhaps $unit) or a module
VL_RESTORER(m_prefix);
2022-12-21 01:22:42 +01:00
VL_RESTORER(m_modp);
m_modp = nodep;
m_prefix = nodep->name() + "__03a__03a"; // ::
iterateChildren(nodep);
}
void visit(AstVar* nodep) override {
iterateChildren(nodep);
if (m_packageScopep) {
if (m_ftaskp && m_ftaskp->isStatic()) {
// Move later, or we wouldn't keep iterating the class
// We're really moving the VarScope but we might not
// have a pointer to it yet
2023-10-28 12:24:04 +02:00
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
}
2024-03-28 01:07:46 +01:00
if (!m_ftaskp && (nodep->lifetime().isStatic() || nodep->isParam())) {
2023-10-28 12:24:04 +02:00
m_toPackageMoves.emplace_back(nodep, m_classPackagep);
2022-01-02 21:09:07 +01:00
// We're really moving the VarScope but we might not
// have a pointer to it yet
2023-10-28 12:24:04 +02:00
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
2022-01-02 21:09:07 +01:00
}
}
}
void visit(AstVarScope* nodep) override {
iterateChildren(nodep);
nodep->varp()->user1p(nodep);
}
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_ftaskp);
m_ftaskp = nodep;
iterateChildren(nodep);
if (m_packageScopep && nodep->isStatic()) {
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
}
}
void visit(AstCFunc* nodep) override {
iterateChildren(nodep);
// Don't move now, or wouldn't keep iterating the class
// TODO move function statics only
// if (m_classScopep) {
2023-10-28 12:24:04 +02:00
// m_toScopeMoves.emplace_back(nodep, m_classScopep);
//}
}
void visit(AstNodeCoverDecl* nodep) override {
2023-03-04 01:26:15 +01:00
// Need to declare coverage in package, where we have access to symbol table
iterateChildren(nodep);
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
}
void visit(AstInitial* nodep) override {
2022-01-02 21:09:07 +01:00
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
2022-01-02 21:09:07 +01:00
}
void visit(AstInitialStatic* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
}
2023-01-28 04:41:12 +01:00
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
// Give struct a pointer to its package and a final name
dtypep->editCountInc();
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
2023-06-07 00:28:35 +02:00
dtypep->name(
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
if (dtypep->packed()) m_strDtypeps.insert(dtypep);
2022-12-21 01:22:42 +01:00
for (AstMemberDType* itemp = dtypep->membersp(); itemp;
2022-12-21 01:22:42 +01:00
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
// Recurse only into anonymous structs inside this definition,
// other structs will be reached from another typedefs
if (subp && subp->name().empty()) setStructModulep(subp);
2022-12-21 01:22:42 +01:00
}
}
void visit(AstTypedef* nodep) override {
if (nodep->user1SetOnce()) return;
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
if (dtypep && dtypep->packed()) {
m_typedefps.insert(nodep);
if (nodep->attrPublic()) m_pubStrDtypeps.push(dtypep);
}
2022-12-21 01:22:42 +01:00
iterateChildren(nodep);
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
2022-12-21 01:22:42 +01:00
if (dtypep) {
2022-12-21 01:22:42 +01:00
dtypep->name(nodep->name());
setStructModulep(dtypep);
}
}
void visit(AstNodeExpr* nodep) override {} // Short circuit
void visit(AstNodeStmt* nodep) override {} // Short circuit
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ClassVisitor(AstNetlist* nodep) { iterate(nodep); }
~ClassVisitor() override {
for (auto moved : m_toScopeMoves) {
AstNode* const nodep = moved.first;
AstScope* const scopep = moved.second;
UINFO(9, "moving " << nodep << " to " << scopep);
if (VN_IS(nodep, NodeFTask)) {
scopep->addBlocksp(nodep->unlinkFrBack());
} else if (VN_IS(nodep, Var)) {
AstVarScope* const vscp = VN_AS(nodep->user1p(), VarScope);
2022-01-02 21:09:07 +01:00
vscp->scopep(scopep);
vscp->unlinkFrBack();
scopep->addVarsp(vscp);
} else if (VN_IS(nodep, Initial) || VN_IS(nodep, InitialStatic)) {
2022-01-02 21:09:07 +01:00
nodep->unlinkFrBack();
scopep->addBlocksp(nodep);
} else {
nodep->v3fatalSrc("Bad case");
}
}
2022-01-02 21:09:07 +01:00
for (auto moved : m_toPackageMoves) {
AstNode* const nodep = moved.first;
AstNodeModule* const modp = moved.second;
UINFO(9, "moving " << nodep << " to " << modp);
2022-01-02 21:09:07 +01:00
nodep->unlinkFrBack();
modp->addStmtsp(nodep);
2022-01-02 21:09:07 +01:00
}
// BFS to mark public typedefs.
std::set<const AstNodeUOrStructDType*> pubStrDtypeps;
while (!m_pubStrDtypeps.empty()) {
AstNodeUOrStructDType* const dtypep = m_pubStrDtypeps.front();
m_pubStrDtypeps.pop();
if (pubStrDtypeps.insert(dtypep).second) {
for (AstMemberDType* itemp = dtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (AstNodeUOrStructDType* const subp = itemp->getChildStructp())
m_pubStrDtypeps.push(subp);
}
}
}
for (AstTypedef* typedefp : m_typedefps) {
const AstNodeUOrStructDType* const sdtypep
= VN_AS(typedefp->dtypep(), NodeUOrStructDType);
if (pubStrDtypeps.count(sdtypep)) typedefp->attrPublic(true);
}
// Clear package pointer of non-public packed struct / union type, which will never be
// exported.
for (AstNodeUOrStructDType* sdtypep : m_strDtypeps) {
if (!pubStrDtypeps.count(sdtypep)) sdtypep->classOrPackagep(nullptr);
}
}
};
//######################################################################
// Class class functions
void V3Class::classAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ ClassVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("class", 0, dumpTreeEitherLevel() >= 3);
}