This commit is contained in:
Igor Zaworski 2026-03-03 10:32:07 -05:00 committed by GitHub
commit 58c8c0fdda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 122 additions and 35 deletions

View File

@ -1937,11 +1937,26 @@ public:
VlClassRef(VlNull){};
template <typename... T_Args>
VlClassRef(VlDeleter& deleter, T_Args&&... args)
// () required here to avoid narrowing conversion warnings,
// when a new() has an e.g. CData type and passed a 1U.
: m_objp{new T_Class(std::forward<T_Args>(args)...)} {
// refCountInc was moved to the constructor of T_Class
// to fix self references in constructor.
: m_objp{new T_Class} {
// Instantly init the object to presevrve RAII
m_objp->init(std::forward<T_Args>(args)...);
m_objp->m_deleterp = &deleter;
}
VlClassRef(VlDeleter& deleter, T_Class&& args)
// Move constructor
: m_objp{new T_Class{std::forward<T_Class>(args)}} {
m_objp->m_deleterp = &deleter;
}
VlClassRef(VlDeleter& deleter, const T_Class& args)
// Copy constructor
: m_objp{new T_Class{args}} {
m_objp->m_deleterp = &deleter;
}
VlClassRef(VlDeleter& deleter, T_Class& args)
// Copy constructor - this is required since if `T_Class&`
// will be provided a compiler will match it to the constructor
// with variadic template instead of `T_Class&&`
: m_objp{new T_Class{args}} {
m_objp->m_deleterp = &deleter;
}
// Explicit to avoid implicit conversion from 0

View File

@ -149,6 +149,10 @@ class CCtorsVisitor final : public VNVisitor {
// VISITORS
void visit(AstNodeModule* nodep) override {
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;
@ -199,6 +203,8 @@ class CCtorsVisitor final : public VNVisitor {
m_varResetp->add(crstp);
} else if (m_cfuncp) {
nodep->addNextHere(crstp);
} else {
nodep->v3fatalSrc("Var needs CReset but nowhere to place it");
}
}
}

View File

@ -78,7 +78,7 @@ string EmitCBaseVisitorConst::funcNameProtect(const AstCFunc* nodep, const AstNo
modp = modp ? modp : EmitCParentModule::get(nodep);
string name;
if (nodep->isConstructor()) {
name += EmitCUtil::prefixNameProtect(modp);
name += "init";
} else if (nodep->isDestructor()) {
name += "~";
name += EmitCUtil::prefixNameProtect(modp);
@ -126,10 +126,15 @@ string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) {
return args;
}
void EmitCBaseVisitorConst::emitCDefaultConstructor(const AstNodeModule* const modp) {
puts(EmitCUtil::prefixNameProtect(modp));
puts("() = default;\n");
}
void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
bool withScope) {
if (funcp->slow()) putns(funcp, "VL_ATTR_COLD ");
if (!funcp->isConstructor() && !funcp->isDestructor()) {
if (!funcp->isDestructor()) {
putns(funcp, funcp->rtnTypeVoid());
puts(" ");
}
@ -149,6 +154,7 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo
bool cLinkage) {
ensureNewLine();
if (!funcp->ifdef().empty()) putns(funcp, "#ifdef " + funcp->ifdef() + "\n");
if (funcp->isConstructor()) emitCDefaultConstructor(modp);
if (cLinkage) putns(funcp, "extern \"C\" ");
if (funcp->isStatic() && funcp->isProperMethod()) putns(funcp, "static ");
if (funcp->isVirtual()) {
@ -158,7 +164,9 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo
// on other methods where virtual vs override is needed, and this is not tracked yet
}
emitCFuncHeader(funcp, modp, /* withScope: */ false);
if (funcp->emptyBody() && !funcp->isLoose() && !cLinkage) {
const AstClass* const classp = VN_CAST(modp, Class);
if (funcp->emptyBody() && !funcp->isLoose() && !cLinkage
&& !(funcp->isConstructor() && classp && classp->isInterfaceClass())) {
putns(funcp, " {}\n");
} else {
putns(funcp, ";\n");

View File

@ -183,6 +183,7 @@ public:
static string protect(const string& name) VL_MT_SAFE { return VIdProtect::protect(name); }
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr);
string cFuncArgs(const AstCFunc* nodep);
void emitCDefaultConstructor(const AstNodeModule* modp);
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
void emitVarDecl(const AstVar* nodep, bool asRef = false);

View File

@ -336,25 +336,28 @@ public:
std::unordered_set<AstClass*> doneClasses;
collectVirtualBasesRecursep(classp, virtualBases);
for (AstClass* vbase : virtualBases) {
if (vbase->isInterfaceClass()) continue;
if (doneClasses.count(vbase)) continue;
puts(doneClasses.empty() ? "" : "\n , ");
doneClasses.emplace(vbase);
puts(EmitCUtil::prefixNameProtect(vbase));
puts("::init");
if (constructorNeedsProcess(vbase)) {
puts("(vlProcess, vlSymsp)");
puts("(vlProcess, vlSymsp);");
} else {
puts("(vlSymsp)");
puts("(vlSymsp);");
}
puts("\n");
}
const AstCNew* const superNewCallp = getSuperNewCallRecursep(cfuncp->stmtsp());
// Direct non-virtual bases in declaration order
for (const AstClassExtends* extp = classp->extendsp(); extp;
extp = VN_AS(extp->nextp(), ClassExtends)) {
if (extp->classp()->isInterfaceClass()) continue;
if (extp->classp()->useVirtualPublic()) continue;
if (doneClasses.count(extp->classp())) continue;
puts(doneClasses.empty() ? "" : "\n , ");
doneClasses.emplace(extp->classp());
puts(EmitCUtil::prefixNameProtect(extp->classp()));
puts("::init");
if (constructorNeedsProcess(extp->classp())) {
puts("(vlProcess, vlSymsp");
} else {
@ -364,7 +367,7 @@ public:
if (!extp->classp()->isInterfaceClass() && superNewCallp) {
putCommaIterateNext(superNewCallp->argsp(), true);
}
puts(")");
puts(");\n");
}
}
void collectVirtualBasesRecursep(const AstClass* classp,
@ -403,14 +406,11 @@ public:
if (nodep->ifdef() != "") putns(nodep, "#ifdef " + nodep->ifdef() + "\n");
emitCFuncHeader(nodep, m_modp, /* withScope: */ true);
puts(" {\n");
if (nodep->isConstructor()) {
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
if (classp && classp->extendsp()) {
puts("\n : ");
putConstructorSubinit(classp, nodep);
}
if (classp && classp->extendsp()) putConstructorSubinit(classp, nodep);
}
puts(" {\n");
// "+" in the debug indicates a print from the model
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");

View File

@ -5299,7 +5299,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
<< classExtendsp->argsp()->warnContextSecondary());
}
if (classExtendsp && classExtendsp->classOrNullp()) {
if (!m_explicitSuperNewp && m_statep->forParamed()) {
if (!m_explicitSuperNewp && m_statep->forParamed()
&& !VN_AS(classExtendsp->childDTypep()->skipRefp(), ClassRefDType)
->classp()
->isInterfaceClass()) {
AstNodeStmt* const superNewp
= addImplicitSuperNewCall(VN_AS(nodep, Func), classExtendsp);
UINFO(9, "created super new " << superNewp);

View File

@ -45,9 +45,6 @@ class LocalizeVisitor final : public VNVisitor {
// AstVarScope::user3p() -> Set of CFuncs referencing this VarScope. (via m_accessors)
// AstCFunc::user4p() -> Multimap of 'VarScope -> VarRefs that reference that VarScope'
// in this function. (via m_references)
// AstVarScope::user4() -> Bool indicating VarScope cannot be optimized
// - compared to AstVarScope::user1 this guarantees that this
// scope won't be optimized.
const VNUser1InUse m_user1InUse;
const VNUser3InUse m_user3InUse;
const VNUser4InUse m_user4InUse;
@ -63,7 +60,6 @@ class LocalizeVisitor final : public VNVisitor {
// STATE - for current visit position (use VL_RESTORER)
AstCFunc* m_cfuncp = nullptr; // Current active function
uint32_t m_nodeDepth = 0; // Node depth under m_cfuncp
bool m_inSuperConstructorCallStmt = false; // If under super constructor call statement
// METHODS
bool isOptimizable(AstVarScope* nodep) {
@ -76,7 +72,6 @@ class LocalizeVisitor final : public VNVisitor {
if (nodep->dtypep()->skipRefp()->isString()) return false;
// Variables used in super constructor call can't be localized, because
// in C++ there is no way to declare them before base class constructor call
if (nodep->user4()) return false;
return ((!nodep->user1() // Not marked as not optimizable, or ...
// .. a block temp used in a single CFunc
|| (nodep->varp()->varType() == VVarType::BLOCKTEMP
@ -160,9 +155,6 @@ class LocalizeVisitor final : public VNVisitor {
}
void visit(AstCNew* nodep) override {
VL_RESTORER(m_inSuperConstructorCallStmt);
m_inSuperConstructorCallStmt
= m_cfuncp->isConstructor() && VN_IS(nodep->backp(), StmtExpr);
m_cfuncp->user1(true); // Mark caller as not a leaf function
iterateChildren(nodep);
}
@ -216,11 +208,7 @@ class LocalizeVisitor final : public VNVisitor {
// Remember the reference so we can fix it up later (we always need this as well)
m_references(m_cfuncp).emplace(varScopep, nodep);
if (m_inSuperConstructorCallStmt) {
// Variable used in super constructor call can't be localized
varScopep->user1(true);
varScopep->user4(true);
} else if (!varScopep->user1()) { // Check if already marked as not optimizable
if (!varScopep->user1()) { // Check if already marked as not optimizable
// Note: we only check read variables, as it's ok to localize (and in fact discard)
// any variables that are only written but never read.
if (nodep->access().isReadOrRW() && !varScopep->user2()) {

View File

@ -1338,9 +1338,15 @@ class TaskVisitor final : public VNVisitor {
if (!nodep->dpiImport() && !nodep->taskPublic()) {
// Need symbol table
if (cfuncp->name() == "new") {
const string stmt = VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);";
cfuncp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
if (cfuncp->isConstructor()) {
bool isInterfaceClass = false;
if (const AstClass* const classp = VN_CAST(m_modp, Class)) {
isInterfaceClass = classp->isInterfaceClass();
}
if (!isInterfaceClass) {
const string stmt = VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);";
cfuncp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
}
}
if (nodep->dpiContext()) {

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2026 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,42 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Base;
int k = 3;
function new(int x);
k = x;
endfunction
protected function int get_x_base();
Base f = this;
return 7;
endfunction
endclass
class Foo extends Base;
function new(int x);
super.new(x);
endfunction
protected function int get_x();
Foo f = this;
return get_x_base();
endfunction
endclass
class Bar extends Foo;
function new();
super.new(get_x());
endfunction
endclass
module top;
initial begin
static Bar b = new();
if (b.k != 7) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule