Fix of localize for super constructors with function calls as arguments (#6330)

This commit is contained in:
Igor Zaworski 2025-08-25 12:55:11 +02:00 committed by GitHub
parent ac2a75fbb5
commit c2cac8a7fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 2 deletions

View File

@ -45,6 +45,9 @@ class LocalizeVisitor final : public VNVisitor {
// AstVarScope::user3p() -> Set of CFuncs referencing this VarScope. (via m_accessors) // AstVarScope::user3p() -> Set of CFuncs referencing this VarScope. (via m_accessors)
// AstCFunc::user4p() -> Multimap of 'VarScope -> VarRefs that reference that VarScope' // AstCFunc::user4p() -> Multimap of 'VarScope -> VarRefs that reference that VarScope'
// in this function. (via m_references) // 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 VNUser1InUse m_user1InUse;
const VNUser3InUse m_user3InUse; const VNUser3InUse m_user3InUse;
const VNUser4InUse m_user4InUse; const VNUser4InUse m_user4InUse;
@ -60,11 +63,15 @@ class LocalizeVisitor final : public VNVisitor {
// STATE - for current visit position (use VL_RESTORER) // STATE - for current visit position (use VL_RESTORER)
AstCFunc* m_cfuncp = nullptr; // Current active function AstCFunc* m_cfuncp = nullptr; // Current active function
uint32_t m_nodeDepth = 0; // Node depth under m_cfuncp uint32_t m_nodeDepth = 0; // Node depth under m_cfuncp
bool m_inSuperConstructorCallStmt = false; // If under super constructor call statement
// METHODS // METHODS
bool isOptimizable(AstVarScope* nodep) { bool isOptimizable(AstVarScope* nodep) {
// Don't want to malloc/free the backing store all the time // Don't want to malloc/free the backing store all the time
if (VN_IS(nodep->dtypep(), NBACommitQueueDType)) return false; if (VN_IS(nodep->dtypep(), NBACommitQueueDType)) 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 ... return ((!nodep->user1() // Not marked as not optimizable, or ...
// .. a block temp used in a single CFunc // .. a block temp used in a single CFunc
|| (nodep->varp()->varType() == VVarType::BLOCKTEMP || (nodep->varp()->varType() == VVarType::BLOCKTEMP
@ -147,6 +154,14 @@ class LocalizeVisitor final : public VNVisitor {
iterateChildrenConst(nodep); iterateChildrenConst(nodep);
} }
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);
}
void visit(AstCCall* nodep) override { void visit(AstCCall* nodep) override {
m_cfuncp->user1(true); // Mark caller as not a leaf function m_cfuncp->user1(true); // Mark caller as not a leaf function
iterateChildrenConst(nodep); iterateChildrenConst(nodep);
@ -195,8 +210,11 @@ class LocalizeVisitor final : public VNVisitor {
// Remember the reference so we can fix it up later (we always need this as well) // Remember the reference so we can fix it up later (we always need this as well)
m_references(m_cfuncp).emplace(varScopep, nodep); m_references(m_cfuncp).emplace(varScopep, nodep);
// Check if already marked as not optimizable if (m_inSuperConstructorCallStmt) {
if (!varScopep->user1()) { // 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
// Note: we only check read variables, as it's ok to localize (and in fact discard) // 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. // any variables that are only written but never read.
if (nodep->access().isReadOrRW() && !varScopep->user2()) { if (nodep->access().isReadOrRW() && !varScopep->user2()) {

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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,29 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Base;
int j;
function new(int x);
j = x;
endfunction
static function int get_default();
return 8;
endfunction
endclass
class Derived extends Base;
function new();
super.new(get_default());
endfunction
endclass
module t;
initial begin
Derived d = new;
if (d.j != 8) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule