Fix error on out-of-range lvalue part select (#5820).

This commit is contained in:
Wilson Snyder 2025-03-08 11:05:58 -05:00
parent 20b964a9a2
commit 51fcb881d5
6 changed files with 76 additions and 8 deletions

View File

@ -27,6 +27,7 @@ Verilator 5.035 devel
* Fix invalid code motion over branches (#5811) (#5814). [Geza Lore]
* Fix sorting of wide SenItems (#5816). [Geza Lore]
* Fix tcmalloc static link and non-22.04 builds (#5817) (#5818). [Geza Lore]
* Fix error on out-of-range lvalue part select (#5820).
* Fix UNOPTFLAT warnings with `--coverage-trace` and always_comb (#5821).
* Fix function locals in SenExprBuilder (#5822). [Geza Lore]
* Fix type_id package scope resolution (#5826). [Krzysztof Bieganski, Antmicro Ltd.]

View File

@ -542,6 +542,7 @@ public:
// Know no children, and hot function, so skip iterator for speed
// cppcheck-suppress functionConst
void iterateChildren(VNVisitorConst& v) {}
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
};
// === Concrete node types =====================================================

View File

@ -824,7 +824,6 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
}
} else if (AstArraySel* const arraySelp = VN_CAST(nodep, ArraySel)) {
if (AstVar* const p = scVarRecurse(arraySelp->fromp())) return p;
if (AstVar* const p = scVarRecurse(arraySelp->bitp())) return p;
}
return nullptr;
}
@ -2484,6 +2483,22 @@ void AstNodeVarRef::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "access", access().ascii());
dumpJsonGen(str);
}
AstNodeVarRef* AstNodeVarRef::varRefLValueRecurse(AstNode* nodep) {
// Given a (possible) lvalue expression, recurse to find the being-set NodeVarRef, else nullptr
if (AstNodeVarRef* const anodep = VN_CAST(nodep, NodeVarRef)) return anodep;
if (AstNodeSel* const anodep = VN_CAST(nodep, NodeSel))
return varRefLValueRecurse(anodep->fromp());
if (AstSel* const anodep = VN_CAST(nodep, Sel))
return varRefLValueRecurse(anodep->fromp());
if (AstArraySel* const anodep = VN_CAST(nodep, ArraySel))
return varRefLValueRecurse(anodep->fromp());
if (AstMemberSel* const anodep = VN_CAST(nodep, MemberSel))
return varRefLValueRecurse(anodep->fromp());
if (AstStructSel* const anodep = VN_CAST(nodep, StructSel))
return varRefLValueRecurse(anodep->fromp());
return nullptr;
}
void AstVarXRef::dump(std::ostream& str) const {
this->AstNodeVarRef::dump(str);
str << ".=" << dotted() << " ";

View File

@ -962,6 +962,7 @@ class WidthVisitor final : public VNVisitor {
return;
}
UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel
if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() < nodep->lsbConst()) {
// Likely impossible given above width check
nodep->v3warn(E_UNSUPPORTED,
@ -1027,20 +1028,33 @@ class WidthVisitor final : public VNVisitor {
// evaluating type sizes for a generate block condition. We
// should only trigger the error if the out-of-range access is
// actually generated.
AstNodeVarRef* lrefp = AstNodeVarRef::varRefLValueRecurse(nodep);
if (m_doGenerate) {
UINFO(5, "Selection index out of range inside generate." << endl);
UINFO(5, "Selection index out of range inside generate\n");
} else {
nodep->v3warn(SELRANGE, "Selection index out of range: "
<< nodep->msbConst() << ":" << nodep->lsbConst()
<< " outside " << frommsb << ":" << fromlsb);
UINFO(1, " Related node: " << nodep << endl);
}
// Extend it.
const int extendTo = nodep->msbConst() + 1;
AstNodeDType* const subDTypep = nodep->findLogicDType(
extendTo, extendTo, nodep->fromp()->dtypep()->numeric());
widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
false /*noerror*/);
if (lrefp) UINFO(9, " Select extend lrefp " << lrefp << endl);
if (lrefp && lrefp->access().isWriteOrRW()) {
// lvarref[X] = ..., the expression assigned is too wide
// WTF to do
// Don't change the width of this lhsp, instead propagate up
// to upper assign/expression the correct width
AstNodeDType* const subDTypep
= nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
false /*noerror*/);
} else {
// Extend it
const int extendTo = nodep->msbConst() + 1;
AstNodeDType* const subDTypep = nodep->findLogicDType(
extendTo, extendTo, nodep->fromp()->dtypep()->numeric());
widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
false /*noerror*/);
}
}
// iterate FINAL is two blocks above
//
@ -1055,6 +1069,7 @@ class WidthVisitor final : public VNVisitor {
widthCheckSized(nodep, "Extract Range", nodep->lsbp(), selwidthDTypep, EXTEND_EXP,
false /*NOWARN*/);
}
// if (debug() >= 9) nodep->dumpTree("-seldone ");
}
}

View File

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

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t
(
input wire [ 31:0] foo,
output reg [144:0] bar,
output reg [144:0] bar2,
output reg [144:0] bar3,
output reg [144:0] bar4
);
// verilator lint_off SELRANGE
assign bar[159:128] = foo;
assign bar2[159] = foo[1];
assign bar3[159 -: 32] = foo;
assign bar4[128 +: 32] = foo;
endmodule