Fix derived class construction resets inherited per-instance rand_mode state (#8527) (#8528)

This commit is contained in:
Kristof Marien
2026-09-30 11:07:49 -04:00
committed by GitHub
parent c686a25f54
commit 055b870e11
3 changed files with 130 additions and 7 deletions
+18 -7
View File
@@ -4157,17 +4157,28 @@ class RandomizeVisitor final : public VNVisitor {
void makeModeInit(AstVar* modeVarp, AstClass* classp, uint32_t modeCount) {
AstNodeModule* const modeVarModp = VN_AS(modeVarp->user2p(), NodeModule);
FileLine* fl = modeVarp->fileline();
AstVar* const oldSizeVarp = new AstVar{
fl, VVarType::BLOCKTEMP, m_uniqueNames.get("__VoldSize"), modeVarp->findUInt32DType()};
oldSizeVarp->funcLocal(true);
oldSizeVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstCMethodHard* const oldSizep
= new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::READ},
VCMethod::DYN_SIZE, nullptr};
oldSizep->dtypeSetUInt32();
AstCMethodHard* const dynarrayNewp
= new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
VCMethod::DYN_RESIZE, new AstConst{fl, modeCount}};
dynarrayNewp->dtypeSetVoid();
AstNodeFTask* const ctorNewp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
UASSERT_OBJ(ctorNewp, classp, "No new() in class");
// Build init chain: resize -> set-all-to-1 loop
AstNode* const initFirstp = dynarrayNewp->makeStmt();
// Preserve inherited modes set by super.new() and initialize only newly added entries.
AstNode* const initFirstp = oldSizeVarp;
initFirstp->addNext(
makeModeSetLoop(fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
new AstConst{fl, 1}, true));
new AstAssign{fl, new AstVarRef{fl, oldSizeVarp, VAccess::WRITE}, oldSizep});
initFirstp->addNext(dynarrayNewp->makeStmt());
initFirstp->addNext(makeModeSetLoop(
fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE}, new AstConst{fl, 1},
true, new AstVarRef{fl, oldSizeVarp, VAccess::READ}));
// Prepend init code before user statements in constructor body, but after
// var declarations and super.new(). This ensures that user's constraint_mode()
// or rand_mode() calls in the constructor execute after mode arrays are initialized.
@@ -4216,7 +4227,7 @@ class RandomizeVisitor final : public VNVisitor {
newp->addStmtsp(ifp);
}
AstNode* makeModeSetLoop(FileLine* const fl, AstNodeExpr* const lhsp, AstNodeExpr* const rhsp,
bool inTask) {
bool inTask, AstNodeExpr* const startp = nullptr) {
AstVar* const iterVarp = new AstVar{fl, VVarType::BLOCKTEMP, "i", lhsp->findUInt32DType()};
iterVarp->funcLocal(inTask);
iterVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
@@ -4227,8 +4238,8 @@ class RandomizeVisitor final : public VNVisitor {
new AstVarRef{fl, iterVarp, VAccess::READ}};
setp->dtypeSetUInt32();
AstNode* const stmtsp = iterVarp;
stmtsp->addNext(
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, new AstConst{fl, 0}});
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
startp ? startp : new AstConst{fl, 0}});
AstLoop* const loopp = new AstLoop{fl};
stmtsp->addNext(loopp);
+19
View File
@@ -0,0 +1,19 @@
#!/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')
if not test.have_solver:
test.skip("No constraint solver installed")
test.compile()
test.execute()
test.passes()
@@ -0,0 +1,93 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// 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
typedef enum int {
FIRST,
SECOND,
THIRD
} Kind;
class Base;
rand Kind kind;
constraint c_kind { kind inside {FIRST, SECOND, THIRD}; }
endclass
class Intermediate extends Base;
rand int value;
constraint c_value { value == 31; }
function new;
kind = THIRD;
kind.rand_mode(0);
endfunction
endclass
class Derived extends Intermediate;
rand int extra;
constraint c_extra { extra == 15; }
endclass
class CombinedModes;
rand int value;
constraint c_value { value == 7; }
function new;
value = 9;
value.rand_mode(0);
c_value.constraint_mode(0);
endfunction
endclass
module t;
initial begin
automatic Intermediate intermediate = new;
automatic Derived derived = new;
automatic CombinedModes combined_modes = new;
automatic int randomize_result;
`checkd(combined_modes.value, 9)
`checkd(combined_modes.value.rand_mode(), 0)
`checkd(combined_modes.c_value.constraint_mode(), 0)
randomize_result = combined_modes.randomize();
`checkd(randomize_result, 1)
`checkd(combined_modes.value, 9)
`checkd(intermediate.kind, THIRD)
`checkd(intermediate.kind.rand_mode(), 0)
`checkd(derived.kind, THIRD)
`checkd(derived.kind.rand_mode(), 0)
repeat (20) begin
randomize_result = intermediate.randomize();
`checkd(randomize_result, 1)
`checkd(intermediate.kind, THIRD)
`checkd(intermediate.kind.rand_mode(), 0)
`checkd(intermediate.value, 31)
`checkd(intermediate.value.rand_mode(), 1)
randomize_result = derived.randomize();
`checkd(randomize_result, 1)
`checkd(derived.kind, THIRD)
`checkd(derived.kind.rand_mode(), 0)
`checkd(derived.value, 31)
`checkd(derived.value.rand_mode(), 1)
`checkd(derived.extra, 15)
`checkd(derived.extra.rand_mode(), 1)
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule