Merge 5455d294dc into f232449252
This commit is contained in:
commit
58c8c0fdda
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(\"+ ");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue