Optimize emitting to_string() for compiler speedup (#7468)

Signed-off-by: Jakub Michalski <jmichalski@antmicro.com>
This commit is contained in:
Jakub Michalski 2026-05-12 15:49:21 +02:00 committed by GitHub
parent a5a1ed55d3
commit 05f6db7970
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 720 additions and 21 deletions

View File

@ -239,6 +239,7 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
bool m_packed;
bool m_isFourstate = false; // V3Width computes; true if any member is 4-state
bool m_constrainedRand = false; // True if struct has constraint expression
bool m_emitToString = false; // Generate to_string() for this struct/union if set
protected:
AstNodeUOrStructDType(VNType t, FileLine* fl, VSigning numericUnpack)
@ -293,6 +294,8 @@ public:
void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; }
bool isConstrainedRand() const { return m_constrainedRand; }
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
bool emitToString() const { return m_emitToString; }
void setEmitToString() { m_emitToString = true; }
};
// === Concrete node types =====================================================

View File

@ -2663,6 +2663,7 @@ class AstClass final : public AstNodeModule {
bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
public:
AstClass(FileLine* fl, const string& name, const string& libname)
@ -2690,6 +2691,8 @@ public:
void needRNG(bool flag) { m_needRNG = flag; }
bool useVirtualPublic() const { return m_useVirtualPublic; }
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
void markPrintedFrom() { m_printedFrom = true; }
bool isPrintedFrom() const { return m_printedFrom; }
// Return true if this class is an extension of base class (SLOW)
// Accepts nullptrs
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);

View File

@ -2003,6 +2003,7 @@ void AstClass::dump(std::ostream& str) const {
if (isInterfaceClass()) str << " [IFCCLS]";
if (isVirtual()) str << " [VIRT]";
if (useVirtualPublic()) str << " [VIRPUB]";
if (isPrintedFrom()) str << " [PRINTED]";
}
void AstClass::dumpJson(std::ostream& str) const {
// dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file
@ -2010,6 +2011,7 @@ void AstClass::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isExtended);
dumpJsonBoolFuncIf(str, isInterfaceClass);
dumpJsonBoolFuncIf(str, isVirtual);
dumpJsonBoolFuncIf(str, isPrintedFrom);
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
dumpJsonGen(str);
}
@ -2608,10 +2610,12 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
if (packed()) str << " [PACKED]";
if (isFourstate()) str << " [4STATE]";
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
if (emitToString()) str << " [EMITSTR]";
}
void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, packed);
dumpJsonBoolFuncIf(str, isFourstate);
dumpJsonBoolFuncIf(str, emitToString);
dumpJsonGen(str);
}
void AstUnionDType::dump(std::ostream& str) const {

View File

@ -16,7 +16,16 @@
// V3Common's Transformations:
//
// Each class:
// Create string access functions
// Emit to_string() if given class is printed from (used as format arg), or any of its
// parents/children is printed from, or if any of its children has another parent that is
// printed from. If class emits to_string(), emit to_string() for its struct/union/interface
// fields.
// Each struct/union:
// Emit to_string() if given struct/union is used as format arg or is a field of a
// class/struct/union that emits to_string(). If struct/union emits to_string(), emit
// to_string() for its struct/union fields.
// Each interface:
// Emit to_string() if given interface is a field of a class that emits to_string()
//
//*************************************************************************
@ -150,30 +159,192 @@ static void makeToStringMiddle(AstClass* nodep) {
nodep->addStmtsp(funcp);
}
class ToStringVisitor final : public VNVisitorConst {
// NODE STATE
// AstNode::user1() -> bool. to_string() was emitted
// AstClass::user2() -> PrintedFromParent. Used to propagate emission of to_string() from
// parent to child
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
size_t m_emitTowardsRoot
= 0; // class that require its ancestors to emit to_string() sets this
size_t m_emitForMembers = 0; // class/struct that requires its members (and their members
// recursively) to emit to_string() sets this
enum class PrintedFromParent : uint8_t {
UNRESOLVED,
PRINTED_FROM_PARENT,
NO_PRINTED_FROM_PARENT
};
VDouble0 m_statClassToString;
VDouble0 m_statInterfaceToString;
VDouble0 m_statStructUnionToString;
void markClassIterate(AstClass* classp) {
if (classp->user1SetOnce()) return;
VL_RESTORER(m_emitForMembers);
++m_emitForMembers;
for (AstNode* stmtp = classp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
AstNodeDType* nodeDtypep = varp->dtypep();
if (AstIfaceRefDType* const ifaceRedDTypep = VN_CAST(nodeDtypep, IfaceRefDType)) {
if (ifaceRedDTypep->ifacep()) iterateConst(ifaceRedDTypep->ifacep());
} else {
while (nodeDtypep && nodeDtypep->subDTypep()
&& nodeDtypep->subDTypep()->skipRefp()) {
nodeDtypep = nodeDtypep->subDTypep()->skipRefp();
if (AstIfaceRefDType* const ifaceRedDTypep
= VN_CAST(nodeDtypep, IfaceRefDType)) {
if (ifaceRedDTypep->ifacep()) {
iterateConst(ifaceRedDTypep->ifacep());
}
} else if (AstNodeUOrStructDType* const uOrStructDTypep
= VN_CAST(nodeDtypep, NodeUOrStructDType)) {
iterateConst(uOrStructDTypep);
}
}
}
}
}
makeToString(classp);
makeToStringMiddle(classp);
++m_statClassToString;
}
void visit(AstNodeUOrStructDType* structp) override {
if (structp->user1())
return; // if to_string() was already emitted skip the rest of the function
if (structp->emitToString()) ++m_emitForMembers;
if (m_emitForMembers
> 0) { // either this struct directly requires emission of to_string() or the
// class/struct that has this struct as a field requires it to emit to_string()
for (AstMemberDType* memberp = structp->membersp(); memberp;
memberp = VN_AS(memberp->nextp(), MemberDType)) {
AstNodeDType* nodeDtypep = memberp->dtypep();
if (AstNodeUOrStructDType* const uOrStructDTypep
= VN_CAST(nodeDtypep, NodeUOrStructDType)) {
iterateConst(uOrStructDTypep);
} else {
while (nodeDtypep && nodeDtypep->subDTypep()) {
nodeDtypep = nodeDtypep->subDTypep()->skipRefp();
if (AstNodeUOrStructDType* const uOrStructDTypep
= VN_CAST(nodeDtypep, NodeUOrStructDType)) {
iterateConst(uOrStructDTypep);
}
}
}
}
if (structp->emitToString()) --m_emitForMembers;
if (!structp->packed()) {
makeVlToString(structp);
++m_statStructUnionToString;
}
structp->user1(true); // mark that to_string() was emitted to not emit it twice (if
// e.g two printed classes have this struct as a field)
}
}
void visit(AstIface* ifacep) override {
if (ifacep->user1())
return; // if to_string() was already emitted skip the rest of the function
if (m_emitForMembers
> 0) { // class that has this iface as a field requires it to emit to_string()
makeVlToString(ifacep);
++m_statInterfaceToString;
ifacep->user1(true); // mark that to_string() was emitted to not emit it twice (if e.g
// two printed classes have this interface as a field)
}
}
// go towards class hierarchy root, if there is any printed from class on the way, mark its
// ancestors, and mark everything on the return path from the printed from class
void visit(AstClass* classp) override {
if (classp->user1()) return;
if (classp->isPrintedFrom()) {
markClassIterate(classp);
VL_RESTORER(m_emitTowardsRoot);
++m_emitTowardsRoot;
for (const AstClassExtends* extendp = classp->extendsp(); extendp;
extendp = VN_AS(extendp->nextp(), ClassExtends)) {
if (extendp->classp() && !extendp->classp()->user1()) {
iterateConst(extendp->classp());
}
}
classp->user2(static_cast<int>(PrintedFromParent::PRINTED_FROM_PARENT));
} else {
PrintedFromParent printed_parent = static_cast<PrintedFromParent>(classp->user2());
if (printed_parent != PrintedFromParent::UNRESOLVED && m_emitTowardsRoot == 0) {
return;
}
if (m_emitTowardsRoot > 0) markClassIterate(classp);
bool is_root = true;
for (const AstClassExtends* extendp = classp->extendsp(); extendp;
extendp = VN_AS(extendp->nextp(), ClassExtends)) {
if (extendp->classp()) {
if (static_cast<PrintedFromParent>(extendp->classp()->user2())
== PrintedFromParent::UNRESOLVED
|| (m_emitTowardsRoot > 0 && !extendp->classp()->user1())) {
iterateConst(extendp->classp());
}
if (printed_parent != PrintedFromParent::PRINTED_FROM_PARENT) {
printed_parent
= static_cast<PrintedFromParent>(extendp->classp()->user2());
}
is_root = false;
}
}
if (is_root) printed_parent = PrintedFromParent::NO_PRINTED_FROM_PARENT;
classp->user2(static_cast<int>(printed_parent));
if (printed_parent == PrintedFromParent::PRINTED_FROM_PARENT) {
markClassIterate(classp);
// If one parent is printed from, other parent also has to emit to_string() to
// handle cases like:
// Base IFaceClass (printed from)
// V V
// Derived (this class)
VL_RESTORER(m_emitTowardsRoot);
++m_emitTowardsRoot;
for (const AstClassExtends* extendp = classp->extendsp(); extendp;
extendp = VN_AS(extendp->nextp(), ClassExtends)) {
if (extendp->classp() && !extendp->classp()->user1()) {
iterateConst(extendp->classp());
}
}
}
}
}
void visit(AstConstPool*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
public:
explicit ToStringVisitor(AstNode* nodep) {
if (v3Global.hasPrintedObjects()) {
AstNode::user1ClearTree();
AstNode::user2ClearTree();
iterateConst(nodep);
}
V3Stats::addStat("Optimizations, Class ToString emitted", m_statClassToString);
V3Stats::addStat("Optimizations, Interface ToString emitted", m_statInterfaceToString);
V3Stats::addStat("Optimizations, Struct/union ToString emitted",
m_statStructUnionToString);
}
~ToStringVisitor() override = default;
};
//######################################################################
// V3Common class functions
void V3Common::commonAll() {
UINFO(2, __FUNCTION__ << ":");
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
const VNUser1InUse m_inuser1;
// Create common contents for each module
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (AstClass* const classp = VN_CAST(nodep, Class)) {
// Create ToString methods
makeToString(classp);
makeToStringMiddle(classp);
} else if (AstIface* const ifacep = VN_CAST(nodep, Iface)) {
makeVlToString(ifacep);
}
}
for (AstNode* nodep = v3Global.rootp()->typeTablep()->typesp(); nodep;
nodep = nodep->nextp()) {
if (AstNodeUOrStructDType* const dtypep = VN_CAST(nodep, NodeUOrStructDType)) {
if (!dtypep->packed()) makeVlToString(dtypep);
}
}
ToStringVisitor{v3Global.rootp()};
V3Global::dumpCheckGlobalTree("common", 0, dumpTreeEitherLevel() >= 3);
}

View File

@ -133,6 +133,7 @@ class V3Global final {
bool m_useParallelBuild = false; // Use parallel build for model
bool m_useRandSequence = false; // Has `randsequence`
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
bool m_hasPrintedObjects = false; // Design has format args printed with to_string()
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
// Memory address to short string mapping (for debug)
@ -221,6 +222,8 @@ public:
void useRandSequence(bool flag) { m_useRandSequence = flag; }
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
bool hasPrintedObjects() const { return m_hasPrintedObjects; }
void hasPrintedObjects(bool flag) { m_hasPrintedObjects = flag; }
void saveJsonPtrFieldName(const std::string& fieldName);
void ptrNamesDumpJson(std::ostream& os);
void idPtrMapDumpJson(std::ostream& os);

View File

@ -6415,6 +6415,26 @@ class WidthVisitor final : public VNVisitor {
} else if (dtypep->isString()) {
formatAttr = VFormatAttr::STRING;
} else if (isFormatNonNumericArg(dtypep)) {
if (AstVarRef* const varRefp = VN_CAST(argp, VarRef)) {
if (AstClassRefDType* const classRefp
= VN_CAST(varRefp->dtypep(), ClassRefDType)) {
if (classRefp->classp()) {
classRefp->classp()->markPrintedFrom();
v3Global.hasPrintedObjects(true);
}
} else {
AstNodeDType* nodeDtypep = varRefp->dtypep();
while (nodeDtypep && nodeDtypep->subDTypep()
&& nodeDtypep->subDTypep()->skipRefp()) {
nodeDtypep = nodeDtypep->subDTypep()->skipRefp();
if (AstNodeUOrStructDType* const uOrStructDTypep
= VN_CAST(nodeDtypep, NodeUOrStructDType)) {
uOrStructDTypep->setEmitToString();
v3Global.hasPrintedObjects(true);
}
}
}
}
AstNodeExpr* const newp = new AstToStringN{argp->fileline(), argp};
formatAttr = VFormatAttr::COMPLEX;
argp = newp;

View File

@ -0,0 +1,9 @@
struct: ''{s1:'{a:'h0}, s2:'{'{'{b:'h0}, '{b:'h0}, '{b:'h0}}, '{'{b:'h0}, '{b:'h0}, '{b:'h0}}}, x:'h0}'
class: ''{z:'h0, i1:top.t.i1, i2:'{'{top.t.i2}}, s1:'{a:'h0}, s2:'{'{'{b:'h0}, '{b:'h0}, '{b:'h0}}, '{'{b:'h0}, '{b:'h0}, '{b:'h0}}}}'
class from subclass: ''{derived_a:'h0, base_a:'h0}'
class from superclass: ''{derived2_a:'h0, c2:$unit::Class2, base2_a:'h0}'
class from interface: ''{derived3_a:'h0, base3_a:'h0}'
class from interface 2: ''{derived4_a:'h0, }'
nested class: ''{nested_class_field:'h0}'
classes with shared field types: ''{s1:'{x:'h0}, s2:'{x:'h0}, i:top.t.i3}', ''{s:'{x:'h0}, i:top.t.i3}'
*-* All Finished *-*

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--stats", "-DDISPLAY_OBJECTS"])
test.execute()
test.file_grep(test.stats, r"Optimizations, Struct/union ToString emitted\s+(\d+)", 6)
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)", 13)
test.file_grep(test.stats, r"Optimizations, Interface ToString emitted\s+(\d+)", 3)
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,150 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
typedef struct {
struct {
int a;
} s1;
struct {
int b;
} s2[2][3];
int x;
} struct_t;
interface iface;
int y1;
endinterface
interface iface2;
int y2;
endinterface
class Class;
int z;
virtual iface i1;
virtual iface2 i2[1][1];
struct {
int a;
} s1;
struct {
int b;
} s2[2][3];
endclass
class Class2;
int x;
endclass;
class Base;
int base_a;
endclass
class Derived extends Base;
int derived_a;
endclass
class Base2;
int base2_a;
endclass
class Derived2 extends Base2;
int derived2_a;
Class2 c2;
endclass;
class Base3;
int base3_a;
endclass
interface class Iclass;
endclass
class Derived3 extends Base3 implements Iclass;
int derived3_a;
endclass
interface class Iclass2;
endclass
class Derived4 implements Iclass2;
int derived4_a;
endclass
class OuterClass;
class NestedClass;
int nested_class_field;
endclass
endclass
typedef struct {
int x;
} struct2_t;
interface iface3;
int i;
endinterface
class Class3;
struct2_t s1;
struct2_t s2;
virtual iface3 i;
endclass
class Class4;
struct2_t s;
virtual iface3 i;
endclass
module t;
struct_t s;
iface i1();
iface2 i2();
Class c;
Derived d;
Derived2 d2;
Base2 b2;
Derived3 d3;
Iclass iclass;
Derived4 d4;
Iclass2 iclass2;
OuterClass::NestedClass nc;
Class3 c3;
Class4 c4;
iface3 i3();
initial begin
c = new;
c.i1 = i1;
c.i2[0][0] = i2;
d = new;
d2 = new;
d2.c2 = new;
b2 = d2;
d3 = new;
iclass = d3;
d4 = new;
iclass2 = d4;
nc = new;
c3 = new;
c3.i = i3;
c4 = new;
c4.i = i3;
`ifdef DISPLAY_OBJECTS
$display("struct: '%p'", s);
$display("class: '%p'", c);
$display("class from subclass: '%p'", d);
$display("class from superclass: '%p'", b2);
$display("class from interface: '%p'", iclass);
$display("class from interface 2: '%p'", d4);
$display("nested class: '%p'", nc);
$display("classes with shared field types: '%p', '%p'", c3, c4);
`endif
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,77 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// Base
// V
// Derived1---------------- IClass1
// V V V
// Derived2------ Derived7
// V V V
// Derived3 Derived5 Derived8
// V V V
// Derived4 Derived6 Derived9
interface class IClass1;
endclass
class Base;
int base_field;
endclass
class Derived1 extends Base;
int derived1_field;
endclass
class Derived2 extends Derived1;
int derived2_field;
endclass
class Derived3 extends Derived2;
int derived3_field;
endclass
class Derived4 extends Derived3;
int derived4_field;
endclass
class Derived5 extends Derived2;
int derived5_field;
endclass
class Derived6 extends Derived5;
int derived6_field;
endclass
class Derived7 extends Derived1 implements IClass1;
int derived7_field;
endclass
class Derived8 extends Derived7;
int derived8_field;
endclass
class Derived9 extends Derived8;
int derived9_field;
endclass
module t;
`OBJ_TYPE obj = new;
`PRINTED_FROM_TYPE printed_obj = obj;
`ifdef OBJ_TYPE2
`OBJ_TYPE2 obj2 = new;
`PRINTED_FROM_TYPE2 printed_obj2 = obj2;
`endif
initial begin
$display("'%p'", printed_obj);
`ifdef OBJ_TYPE2
$display("'%p'", printed_obj2);
`endif
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,2 @@
''{derived9_field:'h0, derived8_field:'h0, derived7_field:'h0, derived1_field:'h0, base_field:'h0}'
*-* All Finished *-*

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_to_string_emitted_class_hierarchy.v"
obj_type = "Derived9"
printed_from_type = "IClass1"
expected_to_string_classes = ["Base", "Derived1", "IClass1", "Derived7", "Derived8", "Derived9"]
test.compile(verilator_flags2=[
"--stats", f"-DOBJ_TYPE={obj_type}", f"-DPRINTED_FROM_TYPE={printed_from_type}"
])
for class_name in expected_to_string_classes:
test.file_grep(
f"{test.obj_dir}/{test.vm_prefix}___024unit__03a__03a{class_name}__Vclpkg__0.cpp",
f"std::string {test.vm_prefix}___024unit__03a__03a{class_name}::to_string()")
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)",
len(expected_to_string_classes))
test.execute()
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,2 @@
''{derived2_field:'h0, derived1_field:'h0, base_field:'h0}'
*-* All Finished *-*

View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_to_string_emitted_class_hierarchy.v"
obj_type = "Derived2"
printed_from_type = "Derived2"
expected_to_string_classes = [
"Base", "Derived1", "Derived2", "Derived3", "Derived4", "Derived5", "Derived6"
]
test.compile(verilator_flags2=[
"--stats", f"-DOBJ_TYPE={obj_type}", f"-DPRINTED_FROM_TYPE={printed_from_type}"
])
for class_name in expected_to_string_classes:
test.file_grep(
f"{test.obj_dir}/{test.vm_prefix}___024unit__03a__03a{class_name}__Vclpkg__0.cpp",
f"std::string {test.vm_prefix}___024unit__03a__03a{class_name}::to_string()")
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)",
len(expected_to_string_classes))
test.execute()
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,2 @@
''{derived3_field:'h0, derived2_field:'h0, derived1_field:'h0, base_field:'h0}'
*-* All Finished *-*

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_to_string_emitted_class_hierarchy.v"
obj_type = "Derived3"
printed_from_type = "Derived3"
expected_to_string_classes = ["Base", "Derived1", "Derived2", "Derived3", "Derived4"]
test.compile(verilator_flags2=[
"--stats", f"-DOBJ_TYPE={obj_type}", f"-DPRINTED_FROM_TYPE={printed_from_type}"
])
for class_name in expected_to_string_classes:
test.file_grep(
f"{test.obj_dir}/{test.vm_prefix}___024unit__03a__03a{class_name}__Vclpkg__0.cpp",
f"std::string {test.vm_prefix}___024unit__03a__03a{class_name}::to_string()")
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)",
len(expected_to_string_classes))
test.execute()
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,2 @@
''{derived6_field:'h0, derived5_field:'h0, derived2_field:'h0, derived1_field:'h0, base_field:'h0}'
*-* All Finished *-*

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_to_string_emitted_class_hierarchy.v"
obj_type = "Derived6"
printed_from_type = "Derived6"
expected_to_string_classes = ["Base", "Derived1", "Derived2", "Derived5", "Derived6"]
test.compile(verilator_flags2=[
"--stats", f"-DOBJ_TYPE={obj_type}", f"-DPRINTED_FROM_TYPE={printed_from_type}"
])
for class_name in expected_to_string_classes:
test.file_grep(
f"{test.obj_dir}/{test.vm_prefix}___024unit__03a__03a{class_name}__Vclpkg__0.cpp",
f"std::string {test.vm_prefix}___024unit__03a__03a{class_name}::to_string()")
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)",
len(expected_to_string_classes))
test.execute()
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,3 @@
''{derived7_field:'h0, derived1_field:'h0, base_field:'h0}'
''{derived9_field:'h0, derived8_field:'h0, derived7_field:'h0, derived1_field:'h0, base_field:'h0}'
*-* All Finished *-*

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_to_string_emitted_class_hierarchy.v"
obj_type = "Derived7"
printed_from_type = "Derived7"
obj_type2 = "Derived9"
printed_from_type2 = "Derived9"
expected_to_string_classes = ["Base", "Derived1", "IClass1", "Derived7", "Derived8", "Derived9"]
test.compile(verilator_flags2=[
"--stats", f"-DOBJ_TYPE={obj_type}", f"-DPRINTED_FROM_TYPE={printed_from_type}",
f"-DOBJ_TYPE2={obj_type2}", f"-DPRINTED_FROM_TYPE2={printed_from_type2}"
])
for class_name in expected_to_string_classes:
test.file_grep(
f"{test.obj_dir}/{test.vm_prefix}___024unit__03a__03a{class_name}__Vclpkg__0.cpp",
f"std::string {test.vm_prefix}___024unit__03a__03a{class_name}::to_string()")
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)",
len(expected_to_string_classes))
test.execute()
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,3 @@
''{derived4_field:'h0, derived3_field:'h0, derived2_field:'h0, derived1_field:'h0, base_field:'h0}'
''{derived9_field:'h0, derived8_field:'h0, derived7_field:'h0, derived1_field:'h0, base_field:'h0}'
*-* All Finished *-*

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_to_string_emitted_class_hierarchy.v"
obj_type = "Derived4"
printed_from_type = "Derived4"
obj_type2 = "Derived9"
printed_from_type2 = "Derived9"
expected_to_string_classes = [
"Base", "Derived1", "Derived2", "Derived3", "Derived4", "IClass1", "Derived7", "Derived8",
"Derived9"
]
test.compile(verilator_flags2=[
"--stats", f"-DOBJ_TYPE={obj_type}", f"-DPRINTED_FROM_TYPE={printed_from_type}",
f"-DOBJ_TYPE2={obj_type2}", f"-DPRINTED_FROM_TYPE2={printed_from_type2}"
])
for class_name in expected_to_string_classes:
test.file_grep(
f"{test.obj_dir}/{test.vm_prefix}___024unit__03a__03a{class_name}__Vclpkg__0.cpp",
f"std::string {test.vm_prefix}___024unit__03a__03a{class_name}::to_string()")
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)",
len(expected_to_string_classes))
test.execute()
test.files_identical(test.obj_dir + "/vlt_sim.log", test.golden_filename, "logfile")
test.passes()

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t_to_string_emitted.v"
test.compile(verilator_flags2=["--stats"])
test.execute()
test.file_grep(test.stats, r"Optimizations, Struct/union ToString emitted\s+(\d+)", 0)
test.file_grep(test.stats, r"Optimizations, Class ToString emitted\s+(\d+)", 0)
test.file_grep(test.stats, r"Optimizations, Interface ToString emitted\s+(\d+)", 0)
test.passes()