Use unchanged copy of parameterized class for instantation (#4179)

This commit is contained in:
Ryszard Rozak 2023-05-11 09:56:15 +02:00 committed by GitHub
parent e095bf1af0
commit 0198a3fc52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 21 deletions

View File

@ -221,7 +221,7 @@ public:
//######################################################################
// Remove parameters from cells and build new modules
class ParamProcessor final {
class ParamProcessor final : public VNDeleter {
// NODE STATE - Local
// AstVar::user4() // int Global parameter number (for naming new module)
// // (0=not processed, 1=iterated, but no number,
@ -257,6 +257,9 @@ class ParamProcessor final {
// Generated modules by this visitor is not included
V3StringSet m_allModuleNames;
CloneMap m_originalParams; // Map between parameters of copied parameteized classes and their
// original nodes
std::map<const V3Hash, int> m_valueMap; // Hash of node hash to param value
int m_nextValue = 1; // Next value to use in m_valueMap
@ -387,23 +390,20 @@ class ParamProcessor final {
}
return nullptr;
}
void collectPins(CloneMap* clonemapp, AstNodeModule* modp) {
void collectPins(CloneMap* clonemapp, AstNodeModule* modp, bool originalIsCopy) {
// Grab all I/O so we can remap our pins later
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
const AstNode* originalParamp = nullptr;
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
// Cloning saved a pointer to the new node for us, so just follow that link.
const AstVar* const oldvarp = varp->clonep();
// UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp
// <<" -> 0x"<<(uint32_t)varp<<endl);
clonemapp->emplace(oldvarp, varp);
originalParamp = varp->clonep();
}
} else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) {
if (ptp->isGParam()) {
const AstParamTypeDType* const oldptp = ptp->clonep();
clonemapp->emplace(oldptp, ptp);
}
if (ptp->isGParam()) originalParamp = ptp->clonep();
}
if (originalIsCopy) originalParamp = m_originalParams[originalParamp];
clonemapp->emplace(originalParamp, stmtp);
}
}
void relinkPins(const CloneMap* clonemapp, AstPin* startpinp) {
@ -545,9 +545,8 @@ class ParamProcessor final {
}
void replaceRefsRecurse(AstNode* const nodep, const AstClass* const oldClassp,
AstClass* const newClassp) {
// Some of the nodes may be already marked as visited, because they were copied. They
// should be marked as unvisited, because parameterized references have to be handled.
nodep->user5(false);
// Self references linked in the first pass of V3LinkDot.cpp should point to the default
// instance.
if (AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) {
if (classRefp->classp() == oldClassp) classRefp->classp(newClassp);
} else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
@ -565,7 +564,13 @@ class ParamProcessor final {
// Deep clone of new module
// Note all module internal variables will be re-linked to the new modules by clone
// However links outside the module (like on the upper cells) will not.
AstNodeModule* const newmodp = srcModp->cloneTree(false);
AstNodeModule* newmodp;
if (srcModp->user2p()) {
newmodp = VN_CAST(srcModp->user2p()->cloneTree(false), NodeModule);
} else {
newmodp = srcModp->cloneTree(false);
}
if (AstClass* const newClassp = VN_CAST(newmodp, Class)) {
newClassp->isParameterized(false);
replaceRefsRecurse(newmodp->stmtsp(), newClassp, VN_AS(srcModp, Class));
@ -603,7 +608,7 @@ class ParamProcessor final {
// Grab all I/O so we can remap our pins later
// Note we allow multiple users of a parameterized model,
// thus we need to stash this info.
collectPins(clonemapp, newmodp);
collectPins(clonemapp, newmodp, srcModp->user2p());
// Relink parameter vars to the new module
relinkPins(clonemapp, paramsp);
// Fix any interface references
@ -787,6 +792,18 @@ class ParamProcessor final {
}
}
void storeOriginalParams(AstClass* const classp) {
for (AstNode* stmtp = classp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
AstNode* originalParamp = nullptr;
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
if (varp->isGParam()) originalParamp = varp->clonep();
} else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) {
if (ptp->isGParam()) originalParamp = ptp->clonep();
}
if (originalParamp) m_originalParams[stmtp] = originalParamp;
}
}
bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp,
AstPin* pinsp, bool any_overrides) {
// Make sure constification worked
@ -809,9 +826,16 @@ class ParamProcessor final {
if (!any_overrides) {
UINFO(8, "Cell parameters all match original values, skipping expansion.\n");
// Mark that the defeult instance is used.
// It will be checked only if srcModpr is a class.
srcModpr->user2(true);
// If it's the first use of the default instance, create a copy and store it in user2p.
// user2p will also be used to check if the default instance is used.
if (!srcModpr->user2p() && VN_IS(srcModpr, Class)) {
AstClass* classCopyp = VN_AS(srcModpr, Class)->cloneTree(false);
// It is a temporary copy of the original class node, stored in order to create
// another instances. It is needed only during class instantiation.
pushDeletep(classCopyp);
srcModpr->user2p(classCopyp);
storeOriginalParams(classCopyp);
}
} else if (AstNodeModule* const paramedModp
= m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) {
paramedModp->dead(false);
@ -919,7 +943,11 @@ public:
class ParamVisitor final : public VNVisitor {
// NODE STATE
// AstNodeModule::user1 -> bool: already fixed level
// AstClass::user2 -> bool: Referenced (value read only in parameterized classes)
// AstClass::user2p -> AstClass*: Unchanged copy of the parameterized class node.
// The class node may be modified according to parameter
// values and an unchanged copy is needed to instantiate
// classes with different parameters.
// STATE
ParamProcessor m_processor; // De-parameterize a cell, build modules
UnrollStateful m_unroller; // Loop unroller
@ -1377,8 +1405,8 @@ public:
for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp);
for (AstClass* const classp : m_paramClasses) {
if (!classp->user2()) {
// Unreferenced, so it can be removed
if (!classp->user2p()) {
// The default value isn't referenced, so it can be removed
VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp);
} else {
// Referenced. classp became a specialized class with the default

View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2023 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
class Foo #(type T=bit);
int x = $bits(T);
endclass
class Bar #(type S=int) extends Foo#(S);
endclass
typedef Bar#() bar_default_t;
class Baz;
Bar#(logic[7:0]) bar_string;
int bar_x;
function new;
bar_string = new;
bar_x = bar_string.x;
endfunction
endclass
typedef Baz baz_t;
module t;
initial begin
bar_default_t bar_default = new;
baz_t baz = new;
if (bar_default.x != 32) $stop;
if (baz.bar_x != 8) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule