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