Fix static initialization order for packages with class hierarchies (#7324)

This commit is contained in:
Yilou Wang 2026-03-27 17:53:45 +01:00 committed by GitHub
parent 55e5f01758
commit ba9a7aaa66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 146 additions and 1 deletions

View File

@ -369,7 +369,35 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
AstCFunc* createStatic(AstNetlist* netlistp, const LogicClasses& logicClasses) {
AstCFunc* const funcp = util::makeTopFunction(netlistp, "_eval_static", /* slow: */ true);
orderSequentially(funcp, logicClasses.m_static);
const LogicByScope& orig = logicClasses.m_static;
if (orig.size() <= 1) {
orderSequentially(funcp, orig);
return funcp;
}
// Level-based module sorting can reorder packages so that an importing
// package runs before the imported one. Re-sort package entries by source
// file position to restore compilation order (IEEE 1800-2023 26.3).
std::vector<size_t> indices(orig.size());
for (size_t i = 0; i < orig.size(); ++i) indices[i] = i;
std::stable_sort(indices.begin(), indices.end(), [&](size_t a, size_t b) {
const AstNodeModule* const modA = orig[a].first->modp();
const AstNodeModule* const modB = orig[b].first->modp();
const bool isPkgA = VN_IS(modA, Package);
const bool isPkgB = VN_IS(modB, Package);
if (isPkgA != isPkgB) return isPkgA; // Packages before non-packages
if (isPkgA && isPkgB) {
// Sort packages by source file position (compilation order)
return modA->fileline()->operatorCompare(*modB->fileline()) < 0;
}
return false; // Both non-package: preserve original order
});
LogicByScope sorted;
sorted.reserve(orig.size());
for (const size_t i : indices) sorted.emplace_back(orig[i].first, orig[i].second);
orderSequentially(funcp, sorted);
return funcp; // Not splitting yet as it is not final
}

View File

@ -0,0 +1,18 @@
#!/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()
test.execute()
test.passes()

View File

@ -0,0 +1,99 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
// Test package static init ordering with cross-package side effects.
// Mirrors the UVM factory pattern (uvm_coreservice_t / uvm_init).
package pkg_a;
typedef enum int {
STATE_UNINIT = 0,
STATE_INIT = 1,
STATE_DONE = 2
} state_e;
state_e g_state = STATE_UNINIT;
string deferred_q[$];
class service_t;
local static service_t inst;
static function service_t get();
if (inst == null) do_init();
return inst;
endfunction
static function void set(service_t s);
inst = s;
endfunction
endclass
function automatic void do_init();
service_t s;
if (g_state != STATE_UNINIT) return;
g_state = STATE_INIT;
foreach (deferred_q[i]) begin
// Process deferred registrations
end
deferred_q.delete();
s = new();
service_t::set(s);
g_state = STATE_DONE;
endfunction
function void register_type(string name);
if (g_state == STATE_UNINIT) begin
deferred_q.push_back(name);
end
endfunction
endpackage
package pkg_b;
import pkg_a::*;
class component_c;
function new(string name);
service_t s;
s = pkg_a::service_t::get();
endfunction
endclass
component_c default_monitor = new("default_monitor");
endpackage
package pkg_c;
import pkg_a::*;
class registry_common;
static bit m_initialized = deferred_init();
static function bit deferred_init();
if (pkg_a::g_state == STATE_UNINIT) begin
pkg_a::register_type("test_class");
end
return 1;
endfunction
endclass
endpackage
module t;
import pkg_a::*;
import pkg_b::*;
import pkg_c::*;
initial begin
service_t svc;
svc = pkg_a::service_t::get();
`checkd(pkg_a::deferred_q.size(), 0);
`checkd(pkg_a::g_state, 32'd2);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule