Compare commits

...

15 Commits

Author SHA1 Message Date
Greg Davill fd59f49f52
Merge c20a6f4396 into 2ef979a39c 2026-04-21 03:15:18 -05:00
Wilson Snyder 2ef979a39c Fix dead removing packages with only DPI. 2026-04-21 00:34:20 -04:00
Wilson Snyder 23ea3d7f11 Commentary: Changes update 2026-04-21 00:33:40 -04:00
Wilson Snyder 94e3f387a7 Internals: With debug, show cwd 2026-04-20 14:06:01 -04:00
Geza Lore cd30c22d1c
Optimize more Dfg patterns (#7452) 2026-04-20 18:28:11 +01:00
dependabot[bot] 81f6cdc32c
CI: Bump actions/upload-pages-artifact from 4 to 5 in the everything group (#7453) 2026-04-20 19:16:37 +02:00
em2machine 23ca23b7b5
Fix for ariane/CVA6 false member call on object error (#7445) (#7450)
Fixes #7445.
2026-04-20 18:21:59 +02:00
Geza Lore f3c63d017a Optimize Sel patterns in DfgPeephole 2026-04-20 09:01:31 +01:00
Geza Lore ffd2c5c69e Optimize temporary insertion in DfgPeephole 2026-04-20 09:01:31 +01:00
Geza Lore 93d7d9c417 Optimize DfgDataType::hash() 2026-04-20 09:01:31 +01:00
Greg Davill c20a6f4396
Fix whitespace error 2026-04-11 16:03:13 +09:30
Greg Davill 419f3192c5
Avoid greedy calls to userIterateAndNext 2026-04-11 15:32:27 +09:30
Greg Davill e9f42fdfbd
Resolve SelExtract and determine element width from dtype 2026-04-11 15:07:39 +09:30
Greg Davill fee71d420c
Fix array and slice flattening in assignment pattern initialisation 2026-04-11 14:21:40 +09:30
Greg Davill d4613ed1a4
Tests: Add test for array pattern concatenation 2026-04-11 14:21:35 +09:30
26 changed files with 805 additions and 162 deletions

View File

@ -48,7 +48,7 @@ jobs:
ls -lsha
tree -L 3 pages
- name: Upload pages artifact
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@v5
with:
path: pages

12
Changes
View File

@ -65,9 +65,11 @@ Verilator 5.047 devel
* Improve assignment-compatibility type check (#2843) (#5666) (#7052). [Pawel Kojma, Antmicro Ltd.]
* Improve error message when variable used as data type (#7318). [Ryszard Rozak, Antmicro Ltd.]
* Improve E_UNSUPPORTED warning messages (#7329). [Eunseo Song]
* Improve NFA-based multi-cycle SVA evaluation engine (#7430). [Yilou Wang]
* Change array tracing to dump left index to right index (#7205). [Geza Lore, Testorrent USA, Inc.]
* Change `--converge-limit` default to 10000 (#7209).
* Remove DFG extract optimization pass (#7394). [Geza Lore, Testorrent USA, Inc.]
* Remove multi-threaded FST tracing (#7443). [Geza Lore, Testorrent USA, Inc.]
* Optimize trace code for faster compiles on repeated types (#6707) (#6832). [Todd Strader]
* Optimize size of trace declaration object code (#7150). [Szymon Gizler, Antmicro Ltd.]
* Optimize function call return value temporaries (#7152). [Geza Lore, Testorrent USA, Inc.]
@ -80,11 +82,15 @@ Verilator 5.047 devel
* Optimize more patterns in DfgPeephole (#7332). [Geza Lore, Testorrent USA, Inc.]
* Optimize read references in DFG (#7354). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG only once, after scoping (#7362). [Geza Lore, Testorrent USA, Inc.]
* Optimize more DFG peephole patterns (#7423). [Geza Lore, Testorrent USA, Inc.]
* Optimize more DFG peephole patterns (#7423) (#7452). [Geza Lore, Testorrent USA, Inc.]
* Optimize DfgBreakCycles IndependentBits analysis ordering (#7446). [Geza Lore, Testorrent USA, Inc.]
* Optimize select patterns in DfgPeephole. [Geza Lore, Testorrent USA, Inc.]
* Optimize temporary insertion in DfgPeephole. [Geza Lore, Testorrent USA, Inc.]
* Optimize arithmetic right shift (>>>) in DfgBreakCycles (#7447). [Geza Lore, Testorrent USA, Inc.]
* Fix recursive default assignment for sub-arrays (#4589) (#7202). [Julian Carrier]
* Fix virtual interface member trigger convergence (#5116) (#7323). [Yilou Wang]
* Fix shift width mismatch in constraint solver SMT emission (#5420) (#7265). [Yilou Wang]
* Fix returning wrong type from static function in parameterized class (#5479) (#7387) (#7411) (#7418). [em2machine]
* Fix returning wrong type from static function in parameterized class (#5479) (#7387) (#7411) (#7418) (#7445) (#7450). [em2machine]
* Fix randomize size+element queue constraints (#5582) (#7225). [Rahul Behl, Testorrent USA, Inc.]
* Fix null assignment to virtual interfaces (#5974) (#5990). [Maxim Fonarev]
* Fix typedef scope resolution for parameterized class aliases (#5977) (#7319). [Nick Brereton]
@ -151,8 +157,10 @@ Verilator 5.047 devel
* Fix delete inside foreach skipping elements (#7407) (#7410)
* Fix std::randomize in parameterized-derived class (#7409) (#7416). [Yilou Wang]
* Fix virtual interface implied comparison with null (#7421). [Alex Solomatnikov]
* Fix uvm_hdl_release_and_read to release value and check success (#7425). [Christian Hecken, Heidelberg University]
* Fix inline constraint on array-indexed randomize target (#7431) (#7434). [Yilou Wang]
* Fix modification of members of object with const handle (#7433). [Kamil Danecki, Antmicro Ltd.]
* Fix `dist` under implication in constraints (#7440) (#7442). [Alex Solomatnikov] [Yilou Wang]
Verilator 5.046 2026-02-28

View File

@ -58,6 +58,7 @@ Drew Ranck
Drew Taussig
Driss Hafdi
Edgar E. Iglesias
Eric Mejdrich
Eric Müller
Eric Rippey
Eunseo Song

View File

@ -505,7 +505,7 @@ include directories and link to the SystemC libraries.
Deprecated and has no effect.
Before Verialtor 5.048: Optional. Enable multithreaded FST trace; see
In versions before 5.048: Optional. Enable multithreaded FST trace; see
:vlopt:`--trace-threads`.
.. describe:: TRACE_VCD

View File

@ -333,6 +333,7 @@ Muhlestein
Multithreaded
Multithreading
Mykyta
NFA
NOUNOPTFLAT
NaN
Nalbantis
@ -837,6 +838,7 @@ gotFinish
goto
gprof
gtkwave
hdl
hdr
hdzhangdoc
hh
@ -1211,6 +1213,7 @@ upcasting
urandom
uselib
utimes
uvm
uwire
uwires
valgrind

View File

@ -329,8 +329,12 @@ class DeadVisitor final : public VNVisitor {
void visit(AstNodeFTask* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (!nodep->taskPublic() && !nodep->dpiExport() && !nodep->dpiImport())
if (nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()) {
if (m_modp && !m_modp->dead() && !m_modp->verilatorLib())
m_modp->user1Inc(); // Keep container
} else {
m_tasksp.push(nodep);
}
if (nodep->classOrPackagep()) {
if (m_elimCells) {
nodep->classOrPackagep(nullptr);

View File

@ -125,6 +125,8 @@ public:
// Thanks to the interning, equality is identity
bool operator==(const DfgDataType& that) const { return this == &that; }
bool operator!=(const DfgDataType& that) const { return this != &that; }
// Similarly for hash
V3Hash hash() const { return V3Hash{this}; }
// Type of elements, for arrays only
const DfgDataType& elemDtype() const {
@ -132,13 +134,6 @@ public:
return *m_elemDtypep;
}
V3Hash hash() const {
V3Hash hash{static_cast<uint32_t>(m_kind)};
hash += m_size;
if (m_elemDtypep) hash += m_elemDtypep->hash();
return hash;
}
//-----------------------------------------------------------------------
// Static factory and management functions

View File

@ -208,6 +208,9 @@ class V3DfgPeephole final : public DfgVisitor {
size_t m_currentGeneration = 0; // Current generation number
size_t m_lastId = 0; // Last unique vertex ID assigned
size_t m_nTemps = 0; // Number of temporary variables created
// Scope for transient temporariy variables cerated in this pass. They should all be
// eliminated wihtin this pass, so anything should be ok, pick the top scope as easy to find.
AstScope* const m_tmpScopep = v3Global.rootp()->topScopep()->scopep();
// STATIC STATE
static V3DebugBisect s_debugBisect; // Debug aid
@ -404,6 +407,12 @@ class V3DfgPeephole final : public DfgVisitor {
return make<Vertex>(examplep->fileline(), examplep->dtype(), operands...);
}
// Replicate 'bitp' to 'vtxp->width()' bits
DfgVertex* replicate(DfgVertex* vtxp, DfgVertex* bitp) {
if (vtxp->dtype() == m_bitDType) return bitp;
return make<DfgReplicate>(vtxp, bitp, makeI32(vtxp->fileline(), vtxp->width()));
}
// Check two vertex are the same, or the same constant value
static bool isSame(const DfgVertex* ap, const DfgVertex* bp) {
if (ap == bp) return true;
@ -561,15 +570,25 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
// Attempt to reuse associative binary expressions if hey already exist, e.g.:
// '(a OP (b OP c))' -> '(a OP b) OP c', iff '(a OP b)' already exists, or
// '(a OP c) OP b' iff '(a OP c)' already exists and the vertex is commutative.
// Only do this is 'b OP c' has a single use and can subsequently be removed,
// otherwise there is no improvement.
if (rSamep && !rSamep->hasMultipleSinks()) {
DfgVertex* const rlVtxp = rSamep->lhsp();
DfgVertex* const rrVtxp = rSamep->rhsp();
if VL_CONSTEXPR_CXX17 (IsCommutative<Vertex>::value) {
if (!lhsp->hasMultipleSinks() && rlVtxp->hasMultipleSinks()) {
APPLYING(ROTATE_ASSOC_COMM_MULTIUSE) {
replace(make<Vertex>(vtxp, rlVtxp, make<Vertex>(vtxp, lhsp, rrVtxp)));
return true;
}
}
}
// Attempt to reuse associative binary expressions if hey already exist, e.g.:
// '(a OP (b OP c))' -> '(a OP b) OP c', iff '(a OP b)' already exists, or
// '(a OP c) OP b' iff '(a OP c)' already exists and the vertex is commutative.
// Only do this if 'b OP c' has a single use and can subsequently be removed,
// otherwise there is no improvement.
// '(a OP (b OP c))' -> '(a OP b) OP c'
if (Vertex* const existingp
= m_cache.get<Vertex>(resultDType<Vertex>(lhsp, rlVtxp), lhsp, rlVtxp)) {
@ -700,10 +719,10 @@ class V3DfgPeephole final : public DfgVisitor {
tryPushBitwiseOpThroughConcat(Vertex* const vtxp, DfgConst* constp, DfgConcat* concatp) {
FileLine* const flp = vtxp->fileline();
// If at least one of the sides of the Concat constant, or width 1 (i.e.: can be
// further simplified), then push the Vertex past the Concat
if (concatp->lhsp()->is<DfgConst>() || concatp->rhsp()->is<DfgConst>() //
|| concatp->lhsp()->dtype() == m_bitDType || concatp->rhsp()->dtype() == m_bitDType) {
// If at least one of the sides of the Concat constant, then push Vertex past Concat
DfgConst* const catLConstp = concatp->lhsp()->cast<DfgConst>();
DfgConst* const catRConstp = concatp->rhsp()->cast<DfgConst>();
if (catLConstp || catRConstp) {
APPLYING(PUSH_BITWISE_OP_THROUGH_CONCAT) {
const uint32_t width = concatp->width();
const DfgDataType& lDtype = concatp->lhsp()->dtype();
@ -712,14 +731,30 @@ class V3DfgPeephole final : public DfgVisitor {
const uint32_t rWidth = rDtype.size();
// The new Lhs vertex
DfgConst* const newLhsConstp = makeZero(constp->fileline(), lWidth);
newLhsConstp->num().opSel(constp->num(), width - 1, rWidth);
Vertex* const newLhsp = make<Vertex>(flp, lDtype, newLhsConstp, concatp->lhsp());
DfgVertex* const newLhsp = [&]() -> DfgVertex* {
DfgConst* const newLhsConstp = makeZero(constp->fileline(), lWidth);
if (catLConstp) {
V3Number num{constp->fileline(), static_cast<int>(lWidth), 0u};
num.opSel(constp->num(), width - 1, rWidth);
foldOp<Vertex>(newLhsConstp->num(), num, catLConstp->num());
return newLhsConstp;
}
newLhsConstp->num().opSel(constp->num(), width - 1, rWidth);
return make<Vertex>(flp, lDtype, newLhsConstp, concatp->lhsp());
}();
// The new Rhs vertex
DfgConst* const newRhsConstp = makeZero(constp->fileline(), rWidth);
newRhsConstp->num().opSel(constp->num(), rWidth - 1, 0);
Vertex* const newRhsp = make<Vertex>(flp, rDtype, newRhsConstp, concatp->rhsp());
DfgVertex* const newRhsp = [&]() -> DfgVertex* {
DfgConst* const newRhsConstp = makeZero(constp->fileline(), rWidth);
if (catRConstp) {
V3Number num{constp->fileline(), static_cast<int>(rWidth), 0u};
num.opSel(constp->num(), rWidth - 1, 0);
foldOp<Vertex>(newRhsConstp->num(), num, catRConstp->num());
return newRhsConstp;
}
newRhsConstp->num().opSel(constp->num(), rWidth - 1, 0);
return make<Vertex>(flp, rDtype, newRhsConstp, concatp->rhsp());
}();
// Replace this vertex
replace(make<DfgConcat>(concatp, newLhsp, newRhsp));
@ -794,6 +829,46 @@ class V3DfgPeephole final : public DfgVisitor {
return false;
}
template <typename Bitwise>
VL_ATTR_WARN_UNUSED_RESULT bool tryPushBitwiseOpThrougSel(Bitwise* const vtxp) {
DfgVertex* const lhsp = vtxp->lhsp();
DfgVertex* const rhsp = vtxp->rhsp();
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
DfgSel* rSelp = nullptr;
DfgVertex* extrap = nullptr;
if (DfgSel* const selp = rhsp->cast<DfgSel>()) {
rSelp = selp;
} else if (Bitwise* const bitwisep = rhsp->cast<Bitwise>()) {
if (DfgSel* const rlSelp = bitwisep->lhsp()->template cast<DfgSel>()) {
rSelp = rlSelp;
extrap = bitwisep->rhsp();
} else if (DfgSel* const rrSelp = bitwisep->rhsp()->template cast<DfgSel>()) {
rSelp = rrSelp;
extrap = bitwisep->lhsp();
}
}
if (rSelp) {
DfgVertex* const lFromp = lSelp->fromp();
DfgVertex* const rFromp = rSelp->fromp();
if (lFromp->dtype() == rFromp->dtype() && lFromp->width() <= VL_QUADSIZE //
&& lSelp->lsb() == rSelp->lsb()) {
APPLYING(PUSH_BITWISE_THROUGH_SEL) {
Bitwise* const bwp
= make<Bitwise>(vtxp->fileline(), lSelp->fromp()->dtype(),
lSelp->fromp(), rSelp->fromp());
DfgVertex* resp = make<DfgSel>(vtxp, bwp, lSelp->lsb());
if (extrap) resp = make<Bitwise>(vtxp, resp, extrap);
replace(resp);
return true;
}
}
}
}
return false;
}
template <typename Bitwise>
VL_ATTR_WARN_UNUSED_RESULT bool tryReplaceBitwiseWithReduction(Bitwise* vtxp) {
UASSERT_OBJ(vtxp->width() == 1, vtxp, "Width must be 1");
@ -803,13 +878,15 @@ class V3DfgPeephole final : public DfgVisitor {
DfgVertex* const rhsp = vtxp->rhsp();
if (DfgSel* const lSelp = lhsp->template cast<DfgSel>()) {
DfgSel* rSelp = rhsp->template cast<DfgSel>();
DfgSel* rSelp = nullptr;
DfgVertex* extrap = nullptr;
if (!rSelp) {
if (Bitwise* const rBitwisep = rhsp->template cast<Bitwise>()) {
rSelp = rBitwisep->lhsp()->template cast<DfgSel>();
extrap = rBitwisep->rhsp();
}
if (DfgSel* const selp = rhsp->template cast<DfgSel>()) {
rSelp = selp;
} else if (Bitwise* const rBitwisep = rhsp->template cast<Bitwise>()) {
rSelp = rBitwisep->lhsp()->template cast<DfgSel>();
extrap = rBitwisep->rhsp();
} else if (Reduction* const rRedp = rhsp->template cast<Reduction>()) {
rSelp = rRedp->srcp()->template cast<DfgSel>();
}
if (rSelp) {
uint32_t lsb = 0;
@ -1021,6 +1098,94 @@ class V3DfgPeephole final : public DfgVisitor {
return {nullptr, 0, 0};
}
// The following patterns all unwind a Sel throgh it's source and replace it with
// another single Sel. Doing this one at a time can take a long time with nested
// concatenations/selects/etc, so instead unwind as much as possible in one go.
std::pair<DfgVertex*, uint32_t> unwindSel(DfgVertex* fromp, uint32_t lsb,
const uint32_t width) {
while (true) {
const uint32_t msb = lsb + width - 1;
// Sel from Concat
if (DfgConcat* const concatp = fromp->cast<DfgConcat>()) {
DfgVertex* const lhsp = concatp->lhsp();
DfgVertex* const rhsp = concatp->rhsp();
if (msb < rhsp->width()) {
// If the select is entirely from rhs, then replace with sel from rhs
APPLYING(REMOVE_SEL_FROM_RHS_OF_CONCAT) {
fromp = rhsp;
continue;
}
} else if (lsb >= rhsp->width()) {
// If the select is entirely from the lhs, then replace with sel from lhs
APPLYING(REMOVE_SEL_FROM_LHS_OF_CONCAT) {
fromp = lhsp;
lsb -= rhsp->width();
continue;
}
}
}
if (DfgReplicate* const repp = fromp->cast<DfgReplicate>()) {
// If the Sel is wholly into the source of the Replicate, push the Sel through
// the Replicate and apply it directly to the source of the Replicate.
const uint32_t srcWidth = repp->srcp()->width();
if (width <= srcWidth) {
const uint32_t newLsb = lsb % srcWidth;
const uint32_t newMsb = newLsb + width - 1;
if (newMsb < srcWidth) {
APPLYING(PUSH_SEL_THROUGH_REPLICATE) {
fromp = repp->srcp();
lsb = newLsb;
continue;
}
}
}
}
// Sel from Sel
if (DfgSel* const selp = fromp->cast<DfgSel>()) {
APPLYING(REPLACE_SEL_FROM_SEL) {
// Select from the source of the source Sel with adjusted LSB
fromp = selp->fromp();
lsb += selp->lsb();
continue;
}
}
// Sel from a partial variable (including narrowed vertex)
if (DfgVarPacked* const varp = fromp->cast<DfgVarPacked>()) {
if (varp->srcp() && !varp->isVolatile()) {
// Must be a splice, otherwise it would have been inlined
DfgSplicePacked* splicep = varp->srcp()->as<DfgSplicePacked>();
DfgVertex* driverp = nullptr;
uint32_t driverLsb = 0;
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
const uint32_t dMsb = dLsb + src.width() - 1;
// If it does not cover the whole searched bit range, move on
if (lsb < dLsb || dMsb < msb) return false;
// Save the driver
driverp = &src;
driverLsb = dLsb;
return true;
});
if (driverp) {
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
fromp = driverp;
lsb -= driverLsb;
continue;
}
}
}
}
// No patterns matched, stop
break;
}
return {fromp, lsb};
}
// VISIT methods
void visit(DfgVertex*) override {}
@ -1148,38 +1313,12 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
// Sel from Concat
if (DfgConcat* const concatp = fromp->cast<DfgConcat>()) {
DfgVertex* const lhsp = concatp->lhsp();
DfgVertex* const rhsp = concatp->rhsp();
if (msb < rhsp->width()) {
// If the select is entirely from rhs, then replace with sel from rhs
APPLYING(REMOVE_SEL_FROM_RHS_OF_CONCAT) { //
replace(make<DfgSel>(vtxp, rhsp, vtxp->lsb()));
return;
}
} else if (lsb >= rhsp->width()) {
// If the select is entirely from the lhs, then replace with sel from lhs
APPLYING(REMOVE_SEL_FROM_LHS_OF_CONCAT) {
replace(make<DfgSel>(vtxp, lhsp, lsb - rhsp->width()));
return;
}
}
}
if (DfgReplicate* const repp = fromp->cast<DfgReplicate>()) {
// If the Sel is wholly into the source of the Replicate, push the Sel through the
// Replicate and apply it directly to the source of the Replicate.
const uint32_t srcWidth = repp->srcp()->width();
if (width <= srcWidth) {
const uint32_t newLsb = lsb % srcWidth;
if (newLsb + width <= srcWidth) {
APPLYING(PUSH_SEL_THROUGH_REPLICATE) {
replace(make<DfgSel>(vtxp, repp->srcp(), newLsb));
return;
}
}
// Unwind through bit packing in one go
{
const auto res = unwindSel(fromp, lsb, width);
if (res.first != fromp || res.second != lsb) {
replace(make<DfgSel>(vtxp, res.first, res.second));
return;
}
}
@ -1197,15 +1336,6 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
// Sel from Sel
if (DfgSel* const selp = fromp->cast<DfgSel>()) {
APPLYING(REPLACE_SEL_FROM_SEL) {
// Select from the source of the source Sel with adjusted LSB
replace(make<DfgSel>(vtxp, selp->fromp(), lsb + selp->lsb()));
return;
}
}
// Sel from Cond
if (DfgCond* const condp = fromp->cast<DfgCond>()) {
if (!condp->hasMultipleSinks()) {
@ -1239,31 +1369,6 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
}
// Sel from a partial variable (including narrowed vertex)
if (DfgVarPacked* const varp = fromp->cast<DfgVarPacked>()) {
if (varp->srcp() && !varp->isVolatile()) {
// Must be a splice, otherwise it would have been inlined
DfgSplicePacked* splicep = varp->srcp()->as<DfgSplicePacked>();
DfgVertex* driverp = nullptr;
uint32_t driverLsb = 0;
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
const uint32_t dMsb = dLsb + src.width() - 1;
// If it does not cover the whole searched bit range, move on
if (lsb < dLsb || dMsb < msb) return false;
// Save the driver
driverp = &src;
driverLsb = dLsb;
return true;
});
if (driverp) {
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
replace(make<DfgSel>(vtxp, driverp, lsb - driverLsb));
return;
}
}
}
}
}
void visit(DfgMux* const vtxp) override {
@ -1349,18 +1454,27 @@ class V3DfgPeephole final : public DfgVisitor {
if (tryPushBitwiseOpThroughReductions(vtxp)) return;
if (DfgNot* const lhsNotp = lhsp->cast<DfgNot>()) {
if (tryPushBitwiseOpThrougSel(vtxp)) return;
{
DfgNot* const lNotp = lhsp->cast<DfgNot>();
DfgNot* const rNotp = rhsp->cast<DfgNot>();
// ~A & A is all zeroes
if (lhsNotp->srcp() == rhsp) {
if ((lNotp && isSame(lNotp->srcp(), rhsp)) || (rNotp && isSame(lhsp, rNotp->srcp()))) {
APPLYING(REPLACE_CONTRADICTORY_AND) {
replace(makeZero(flp, vtxp->width()));
return;
}
}
// ~A & (A & _) or ~A & (_ & A) is all zeroes
if (DfgAnd* const rhsAndp = rhsp->cast<DfgAnd>()) {
if (lhsNotp->srcp() == rhsAndp->lhsp() || lhsNotp->srcp() == rhsAndp->rhsp()) {
if (DfgAnd* const rSamep = rhsp->cast<DfgAnd>()) {
DfgNot* const rlNotp = rSamep->lhsp()->cast<DfgNot>();
DfgNot* const rrNotp = rSamep->rhsp()->cast<DfgNot>();
// ~A & (A & _) or ~A & (_ & A) is all zeroes
if ((lNotp && isSame(lNotp->srcp(), rSamep->lhsp()))
|| (lNotp && isSame(lNotp->srcp(), rSamep->rhsp()))
|| (rlNotp && isSame(lhsp, rlNotp->srcp()))
|| (rrNotp && isSame(lhsp, rrNotp->srcp()))) {
APPLYING(REPLACE_CONTRADICTORY_AND_3) {
replace(makeZero(flp, vtxp->width()));
return;
@ -1463,24 +1577,29 @@ class V3DfgPeephole final : public DfgVisitor {
if (tryPushBitwiseOpThroughReductions(vtxp)) return;
if (DfgNot* const lhsNotp = lhsp->cast<DfgNot>()) {
if (tryPushBitwiseOpThrougSel(vtxp)) return;
{
DfgNot* const lNotp = lhsp->cast<DfgNot>();
DfgNot* const rNotp = rhsp->cast<DfgNot>();
// ~A | A is all ones
if (lhsNotp->srcp() == rhsp) {
if ((lNotp && isSame(lNotp->srcp(), rhsp)) || (rNotp && isSame(lhsp, rNotp->srcp()))) {
APPLYING(REPLACE_TAUTOLOGICAL_OR) {
DfgConst* const resp = makeZero(flp, vtxp->width());
resp->num().setAllBits1();
replace(resp);
replace(makeOnes(flp, vtxp->width()));
return;
}
}
// ~A | (A | _) or ~A | (_ | A) is all ones
if (DfgOr* const rhsOrp = rhsp->cast<DfgOr>()) {
if (lhsNotp->srcp() == rhsOrp->lhsp() || lhsNotp->srcp() == rhsOrp->rhsp()) {
if (DfgOr* const rSamep = rhsp->cast<DfgOr>()) {
DfgNot* const rlNotp = rSamep->lhsp()->cast<DfgNot>();
DfgNot* const rrNotp = rSamep->rhsp()->cast<DfgNot>();
// ~A | (A | _) or ~A | (_ | A) is all ones
if ((lNotp && isSame(lNotp->srcp(), rSamep->lhsp()))
|| (lNotp && isSame(lNotp->srcp(), rSamep->rhsp()))
|| (rlNotp && isSame(lhsp, rlNotp->srcp()))
|| (rrNotp && isSame(lhsp, rrNotp->srcp()))) {
APPLYING(REPLACE_TAUTOLOGICAL_OR_3) {
DfgConst* const resp = makeZero(flp, vtxp->width());
resp->num().setAllBits1();
replace(resp);
replace(makeOnes(flp, vtxp->width()));
return;
}
}
@ -1525,6 +1644,8 @@ class V3DfgPeephole final : public DfgVisitor {
if (tryPushBitwiseOpThroughReductions(vtxp)) return;
if (tryPushBitwiseOpThrougSel(vtxp)) return;
if (vtxp->dtype() == m_bitDType) {
if (tryReplaceBitwiseWithReduction(vtxp)) return;
}
@ -1847,10 +1968,9 @@ class V3DfgPeephole final : public DfgVisitor {
DfgSplicePacked* const sp = new DfgSplicePacked{m_dfg, flp, vtxp->dtype()};
m_vInfo[sp].m_id = ++m_lastId;
sp->addDriver(catp, lsb, flp);
DfgVertex::ScopeCache scopeCache;
AstScope* const scopep = vtxp->scopep(scopeCache, true);
const std::string name = m_dfg.makeUniqueName("PeepholeNarrow", m_nTemps++);
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
DfgVertexVar* const varp
= m_dfg.makeNewVar(flp, name, vtxp->dtype(), m_tmpScopep);
varp->tmpForp(varp->vscp());
m_vInfo[varp].m_id = ++m_lastId;
varp->vscp()->varp()->isInternal(true);
@ -2511,53 +2631,63 @@ class V3DfgPeephole final : public DfgVisitor {
}
if (vtxp->dtype() == m_bitDType) {
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
replace(make<DfgAnd>(vtxp, make<DfgNot>(vtxp, condp), elsep));
return;
}
}
if (thenp == condp) { // a ? a : b becomes a | b
if (isSame(condp, thenp)) { // a ? a : b becomes a | b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) {
replace(make<DfgOr>(vtxp, condp, elsep));
return;
}
}
if (elsep == condp) { // a ? b : a becomes a & b
if (isSame(condp, elsep)) { // a ? b : a becomes a & b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) {
replace(make<DfgAnd>(vtxp, condp, thenp));
return;
}
}
}
if (vtxp->width() <= VL_QUADSIZE) {
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
replace(make<DfgAnd>(vtxp, maskp, elsep));
return;
}
}
if (isOnes(thenp)) { // a ? 1 : b becomes a | b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
replace(make<DfgOr>(vtxp, condp, elsep));
DfgVertex* const maskp = replicate(vtxp, condp);
replace(make<DfgOr>(vtxp, maskp, elsep));
return;
}
}
if (isZero(elsep)) { // a ? b : 0 becomes a & b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
replace(make<DfgAnd>(vtxp, condp, thenp));
DfgVertex* const maskp = replicate(vtxp, condp);
replace(make<DfgAnd>(vtxp, maskp, thenp));
return;
}
}
if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
replace(make<DfgOr>(vtxp, make<DfgNot>(vtxp, condp), thenp));
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
replace(make<DfgOr>(vtxp, maskp, thenp));
return;
}
}
if (DfgOr* const tOrp = thenp->cast<DfgOr>()) {
if (isSame(tOrp->lhsp(), elsep)) { // a ? b | c : b becomes b | (a & c)
APPLYING(REPLACE_COND_THEN_OR_LHS) {
DfgAnd* const andp = make<DfgAnd>(vtxp, condp, tOrp->rhsp());
DfgVertex* const maskp = replicate(vtxp, condp);
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->rhsp());
replace(make<DfgOr>(vtxp, tOrp->lhsp(), andp));
return;
}
}
if (isSame(tOrp->rhsp(), elsep)) { // a ? b | c : c becomes c | (a & b)
APPLYING(REPLACE_COND_THEN_OR_RHS) {
DfgAnd* const andp = make<DfgAnd>(vtxp, condp, tOrp->lhsp());
DfgVertex* const maskp = replicate(vtxp, condp);
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->lhsp());
replace(make<DfgOr>(vtxp, tOrp->rhsp(), andp));
return;
}
@ -2590,6 +2720,33 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
}
if (!tConcatp->hasMultipleSinks()) {
if (DfgConcat* const tRCatp = tConcatp->rhsp()->cast<DfgConcat>()) {
if (!tRCatp->hasMultipleSinks()) {
if (DfgSel* const tLSelp = tConcatp->lhsp()->cast<DfgSel>()) {
if (DfgSel* const tRRSelp = tRCatp->rhsp()->cast<DfgSel>()) {
if (tLSelp->lsb() == tRCatp->width() //
&& tRRSelp->lsb() == 0 //
&& isSame(tLSelp->fromp(), elsep) //
&& isSame(tRRSelp->fromp(), elsep)) {
APPLYING(REPLACE_COND_INSERT) {
DfgVertex* const newTp = tRCatp->lhsp();
DfgVertex* const newEp = make<DfgSel>(
flp, newTp->dtype(), elsep, tRRSelp->width());
DfgCond* const newCp = make<DfgCond>(flp, newTp->dtype(),
condp, newTp, newEp);
replace(make<DfgConcat>(
vtxp, tLSelp,
make<DfgConcat>(tRCatp, newCp, tRRSelp)));
return;
}
}
}
}
}
}
}
}
if (isEqOne(thenp) && isZero(elsep)) {

View File

@ -51,6 +51,7 @@
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PULL_NOTS_THROUGH_COND) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_OP_THROUGH_CONCAT) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_REDUCTION) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_SEL) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_COMMUTATIVE_BINARY_THROUGH_COND) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_COMPARE_OP_THROUGH_CONCAT) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_CONCAT_THROUGH_COND_LHS) \
@ -110,6 +111,7 @@
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_CONST_ZERO_ONES) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_DEC) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_INC) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_INSERT) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_LHS) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_RHS) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_SAME_CAT_LHS) \
@ -160,6 +162,7 @@
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REUSE_ASSOC_BINARY_LHS_WITH_LHS_OF_RHS) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REUSE_ASSOC_BINARY_LHS_WITH_RHS_OF_RHS) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, RIGHT_LEANING_ASSOC) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, ROTATE_ASSOC_COMM_MULTIUSE) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_COND_WITH_NEQ_CONDITION) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_COND_WITH_NOT_CONDITION) \
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_SIDES_IN_BINARY)

View File

@ -40,7 +40,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Changes user() and weight()
class GraphRemoveRedundant final : GraphAlg<> {
const bool m_sumWeights; ///< Sum, rather then maximize weights
const bool m_sumWeights; // Sum, rather then maximize weights
private:
void main() {
for (V3GraphVertex& vertex : m_graphp->vertices()) vertexIterate(&vertex);

View File

@ -2295,7 +2295,10 @@ void V3Options::setDebugMode(int level) {
if (!m_dumpLevel.count("tree")) m_dumpLevel["tree"] = 3; // Don't override if already set.
m_stats = true;
m_debugCheck = true;
if (level) cout << "Starting " << version() << "\n";
if (level) {
cout << "- Starting " << version() << "\n";
UINFO(1, "Current working directory (CWD) is " << V3Os::cwd());
}
}
unsigned V3Options::debugLevel(const string& tag) const VL_MT_SAFE {

View File

@ -1406,13 +1406,16 @@ class ParamProcessor final {
}
cloneVarp->valuep(exprp->cloneTree(false));
if (AstNodeDType* const origDTypep = modvarp->subDTypep()) {
AstNodeDType* const dtypeClonep = origDTypep->cloneTree(false);
// Inline every param ref so widthing doesn't reach back into the template
// (#7411). Cycle detector for dependent parameters in the same module.
// Attach clone under cloneVarp so the root has a back pointer.
if (cloneVarp->childDTypep())
cloneVarp->childDTypep()->unlinkFrBack()->deleteTree();
cloneVarp->childDTypep(origDTypep->cloneTree(false));
cloneVarp->dtypep(nullptr);
// Inline param refs so widthing doesn't touch the template (#7411).
constexpr int maxSubstIters = 1000;
for (int it = 0; it < maxSubstIters; ++it) {
bool any = false;
dtypeClonep->foreach([&](AstVarRef* varrefp) {
cloneVarp->foreach([&](AstVarRef* varrefp) {
AstVar* const targetp = varrefp->varp();
AstNode* replacep = nullptr;
for (AstPin* pp = paramsp; pp; pp = VN_AS(pp->nextp(), Pin)) {
@ -1432,19 +1435,40 @@ class ParamProcessor final {
any = true;
}
});
// Substitute RefDType to an overridden paramtype. RefDType is
// not a foreach leaf, so collect matches and replace after the
// walk. Reverse order so descendants are replaced before
// ancestors -- replacing an ancestor would free its descendants.
std::vector<std::pair<AstRefDType*, AstNodeDType*>> toReplace;
cloneVarp->foreach([&](AstRefDType* refp) {
AstParamTypeDType* const ptdp
= VN_CAST(refp->refDTypep(), ParamTypeDType);
if (!ptdp) return;
for (AstPin* pp = paramsp; pp; pp = VN_AS(pp->nextp(), Pin)) {
if (pp->modPTypep() == ptdp) {
if (AstNodeDType* const overDtp
= VN_CAST(pp->exprp(), NodeDType)) {
toReplace.emplace_back(refp, overDtp);
}
break;
}
}
});
for (auto it = toReplace.rbegin(); it != toReplace.rend(); ++it) {
AstRefDType* const refp = it->first;
refp->replaceWith(it->second->cloneTree(false));
VL_DO_DANGLING(refp->deleteTree(), refp);
any = true;
}
if (!any) break;
}
// Bail if anything still points at the template.
dtypeClonep->foreach([&](AstVarRef* varrefp) {
cloneVarp->foreach([&](AstVarRef* varrefp) {
varrefp->v3fatalSrc(
"Unresolved VarRef '"
<< varrefp->prettyName() << "' in pin dtype clone. Pin: "
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
});
if (cloneVarp->childDTypep())
cloneVarp->childDTypep()->unlinkFrBack()->deleteTree();
cloneVarp->childDTypep(dtypeClonep);
cloneVarp->dtypep(nullptr);
}
V3Const::constifyParamsEdit(cloneVarp);
if (AstConst* const widthedp = VN_CAST(cloneVarp->valuep(), Const)) {

View File

@ -5517,8 +5517,6 @@ class WidthVisitor final : public VNVisitor {
}
if (patp) {
// Don't want the RHS an array
allConstant &= VN_IS(patp->lhssp(), Const);
patp->dtypep(arrayDtp->subDTypep());
AstNodeExpr* const valuep = patternMemberValueIterate(patp);
if (VN_IS(arrayDtp, UnpackArrayDType)) {
@ -5527,7 +5525,50 @@ class WidthVisitor final : public VNVisitor {
= new AstInitArray{nodep->fileline(), arrayDtp, nullptr};
newp = newap;
}
VN_AS(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
// If valuep is a reference to an array constant (or a
// slice of one), flatten its elements into the target
// array. Width resolution has already run (including
// early resolution in patVectorMap), so slices appear
// as AstSliceSel.
const AstInitArray* subInitp = nullptr;
int flattenLo = 0;
int flattenElements = 0;
if (const auto* vrp = VN_CAST(valuep, NodeVarRef)) {
subInitp = VN_CAST(vrp->varp()->valuep(), InitArray);
if (subInitp) {
if (const auto* adtp
= VN_CAST(vrp->varp()->dtypep()->skipRefp(), NodeArrayDType)) {
flattenElements = adtp->declRange().elements();
}
}
} else if (const auto* slicep = VN_CAST(valuep, SliceSel)) {
if (const auto* vrp = VN_CAST(slicep->fromp(), NodeVarRef)) {
subInitp = VN_CAST(vrp->varp()->valuep(), InitArray);
if (subInitp) {
flattenLo = slicep->declRange().lo();
flattenElements = slicep->declRange().elements();
}
}
}
if (subInitp && flattenElements > 0) {
// Sub-array values are always constant
VL_DO_DANGLING(pushDeletep(valuep), valuep);
for (int sn = 0; sn < flattenElements; ++sn) {
UASSERT_OBJ(entn < range.elements(), nodep,
"Flattened sub-array overflows target array");
VN_AS(newp, InitArray)
->addIndexValuep(ent - range.lo(),
subInitp->getIndexDefaultedValuep(flattenLo + sn)
->cloneTree(false));
if (sn < flattenElements - 1) {
++entn;
ent += range.leftToRightInc();
}
}
} else {
allConstant &= VN_IS(valuep, Const);
VN_AS(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
}
} else { // Packed. Convert to concat for now.
if (!newp) {
newp = valuep;
@ -9549,7 +9590,29 @@ class WidthVisitor final : public VNVisitor {
if (!newEntry) {
patp->v3error("Assignment pattern key used multiple times: " << element);
}
element += range.leftToRightInc();
// For positional members that reference an array (or a slice
// of one), advance by that array/slice's element count so
// subsequent members are mapped correctly. Width-resolve the
// value expression so its dtype is set
int elementAdvance = 1;
if (!patp->keyp()
&& (VN_IS(patp->lhssp(), NodeVarRef) || VN_IS(patp->lhssp(), SelExtract))) {
userIterateAndNext(patp->lhssp(), WidthVP{CONTEXT_DET, PRELIM}.p());
AstNodeExpr* const exprp = patp->lhssp();
if (const AstNodeDType* const dtypep = exprp->dtypep()) {
if (const auto* adtp = VN_CAST(dtypep->skipRefp(), UnpackArrayDType)) {
// Only flatten constant arrays backed by InitArray
const AstNodeVarRef* vrp = VN_CAST(exprp, NodeVarRef);
if (!vrp) {
if (const auto* slicep = VN_CAST(exprp, SliceSel))
vrp = VN_CAST(slicep->fromp(), NodeVarRef);
}
if (vrp && VN_IS(vrp->varp()->valuep(), InitArray))
elementAdvance = adtp->declRange().elements();
}
}
}
element += range.leftToRightInc() * elementAdvance;
}
return patmap;
}

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,159 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Greg Davill
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,
expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
package arr_pkg;
localparam logic [31:0] PKG_ADDRS[3] = '{32'hAA001000, 32'hAA002000, 32'hAA003000};
endpackage
module t ( /*AUTOARG*/);
// Test: array concatenation in pattern initialization
// An array localparam used as a value in another array's pattern
// should have its elements "flattened" into the target array.
localparam logic [31:0] BASE_ADDRS[3] = '{32'h80001000, 32'h80002000, 32'h80003000};
// Sub-array slice at the start
localparam logic [31:0] ALL_ADDRS[4] = '{BASE_ADDRS[0:1], 32'h80003000, 32'h80004000};
// Sub-array slice in the middle
localparam logic [31:0] MID[5] = '{32'hFF, BASE_ADDRS[0:1], 32'hAA, 32'hBB};
// Multiple full sub-arrays
localparam logic [31:0] EXTRA[2] = '{32'hC0, 32'hD0};
localparam logic [31:0] MULTI[5] = '{BASE_ADDRS, EXTRA};
// Sub-array with default (sparse InitArray)
localparam logic [31:0] DFLT[3] = '{default: 32'hDD};
localparam logic [31:0] WITH_DFLT[4] = '{DFLT, 32'hEE};
// Slice at the end
localparam logic [31:0] TAIL[4] = '{32'hAA, 32'hBB, BASE_ADDRS[1:2]};
// Multiple slices combined
localparam logic [31:0] MULTI_SLICE[4] = '{BASE_ADDRS[0:1], BASE_ADDRS[1:2]};
// Single-element slice
localparam logic [31:0] SINGLE[3] = '{BASE_ADDRS[0:0], 32'hAA, 32'hBB};
// Descending-range source array
localparam logic [31:0] DESC[2:0] = '{32'hD0, 32'hD1, 32'hD2};
localparam logic [31:0] WITH_DESC[4] = '{DESC[1:0], 32'hAA, 32'hBB};
// Slice bounds from parameter expressions
localparam int unsigned N = 2;
localparam logic [31:0] PARAM_SLICE[4] = '{BASE_ADDRS[0:N-1], 32'hAA, 32'hBB};
// Multiple param-bounded slices composing a larger array
localparam logic [31:0] SRC_A[4] = '{32'hA0, 32'hA1, 32'hA2, 32'hA3};
localparam logic [31:0] SRC_B[4] = '{32'hB0, 32'hB1, 32'hB2, 32'hB3};
localparam int unsigned NA = 2;
localparam int unsigned NB = 3;
localparam int unsigned TOTAL = NA + NB;
localparam logic [31:0] COMPOSED[TOTAL] = '{SRC_A[0:NA-1], SRC_B[0:NB-1]};
// Test key'd associative array initialisations
localparam logic [31:0] KEY_ARR_A[4] = '{0: BASE_ADDRS[0:1], 2: 32'hF2, 3: 32'hF3};
localparam logic [31:0] KEY_ARR_B[4] = '{2: ALL_ADDRS[1:2], default: 32'h00};
// Keyed pattern where values are indexed from another array param
// the key determines position, not the source array's element count.
localparam logic [31:0] KEYED_FROM_ARR[3] = '{
0: BASE_ADDRS[2], 1: BASE_ADDRS[0], 2: BASE_ADDRS[1]
};
// Package-scoped array as a positional pattern member
localparam logic [31:0] WITH_PKG[4] = '{arr_pkg::PKG_ADDRS, 32'hFF};
// Package-scoped array slice
localparam logic [31:0] PKG_SLICE[4] = '{arr_pkg::PKG_ADDRS[0:1], 32'hAA, 32'hBB};
initial begin
`checkh(ALL_ADDRS[0], 32'h80001000);
`checkh(ALL_ADDRS[1], 32'h80002000);
`checkh(ALL_ADDRS[2], 32'h80003000);
`checkh(ALL_ADDRS[3], 32'h80004000);
`checkh(MID[0], 32'hFF);
`checkh(MID[1], 32'h80001000);
`checkh(MID[2], 32'h80002000);
`checkh(MID[3], 32'hAA);
`checkh(MID[4], 32'hBB);
`checkh(MULTI[0], 32'h80001000);
`checkh(MULTI[1], 32'h80002000);
`checkh(MULTI[2], 32'h80003000);
`checkh(MULTI[3], 32'hC0);
`checkh(MULTI[4], 32'hD0);
`checkh(WITH_DFLT[0], 32'hDD);
`checkh(WITH_DFLT[1], 32'hDD);
`checkh(WITH_DFLT[2], 32'hDD);
`checkh(WITH_DFLT[3], 32'hEE);
`checkh(TAIL[0], 32'hAA);
`checkh(TAIL[1], 32'hBB);
`checkh(TAIL[2], 32'h80002000);
`checkh(TAIL[3], 32'h80003000);
`checkh(MULTI_SLICE[0], 32'h80001000);
`checkh(MULTI_SLICE[1], 32'h80002000);
`checkh(MULTI_SLICE[2], 32'h80002000);
`checkh(MULTI_SLICE[3], 32'h80003000);
`checkh(SINGLE[0], 32'h80001000);
`checkh(SINGLE[1], 32'hAA);
`checkh(SINGLE[2], 32'hBB);
`checkh(WITH_DESC[0], 32'hD2);
`checkh(WITH_DESC[1], 32'hD1);
`checkh(WITH_DESC[2], 32'hAA);
`checkh(WITH_DESC[3], 32'hBB);
`checkh(PARAM_SLICE[0], 32'h80001000);
`checkh(PARAM_SLICE[1], 32'h80002000);
`checkh(PARAM_SLICE[2], 32'hAA);
`checkh(PARAM_SLICE[3], 32'hBB);
`checkh(COMPOSED[0], 32'hA0);
`checkh(COMPOSED[1], 32'hA1);
`checkh(COMPOSED[2], 32'hB0);
`checkh(COMPOSED[3], 32'hB1);
`checkh(COMPOSED[4], 32'hB2);
`checkh(KEY_ARR_A[0], 32'h80001000);
`checkh(KEY_ARR_A[1], 32'h80002000);
`checkh(KEY_ARR_A[2], 32'hF2);
`checkh(KEY_ARR_A[3], 32'hF3);
`checkh(KEY_ARR_B[0], 32'h00);
`checkh(KEY_ARR_B[1], 32'h00);
`checkh(KEY_ARR_B[2], 32'h80002000);
`checkh(KEY_ARR_B[3], 32'h80003000);
`checkh(KEYED_FROM_ARR[0], 32'h80003000);
`checkh(KEYED_FROM_ARR[1], 32'h80001000);
`checkh(KEYED_FROM_ARR[2], 32'h80002000);
`checkh(WITH_PKG[0], 32'hAA001000);
`checkh(WITH_PKG[1], 32'hAA002000);
`checkh(WITH_PKG[2], 32'hAA003000);
`checkh(WITH_PKG[3], 32'hFF);
`checkh(PKG_SLICE[0], 32'hAA001000);
`checkh(PKG_SLICE[1], 32'hAA002000);
`checkh(PKG_SLICE[2], 32'hAA);
`checkh(PKG_SLICE[3], 32'hBB);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,6 +1,6 @@
%Error-UNSUPPORTED: t/t_assert_consec_rep_unsup.v:11:45: Unsupported: multi-cycle sequence expression inside consecutive repetition (IEEE 1800-2023 16.9.2)
%Error-UNSUPPORTED: t/t_assert_consec_rep_unsup.v:13:45: Unsupported: multi-cycle sequence expression inside consecutive repetition (IEEE 1800-2023 16.9.2)
: ... note: In instance 't'
11 | assert property (@(posedge clk) (a ##1 b) [* 2] |-> a);
13 | assert property (@(posedge clk) (a ##1 b) [* 2] |-> a);
| ^~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -4,7 +4,9 @@
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
module t (input clk);
module t (
input clk
);
logic a, b;
// Unsupported: multi-cycle sequence expression inside consecutive repetition

View File

@ -250,10 +250,10 @@ module t (
`signal(REUSE_ASSOC_LHS_WITH_RHS_OF_RHS_XOR_COMMON, rand_a[23:4] ^ rand_a[39:20]);
`signal(REUSE_ASSOC_LHS_WITH_RHS_OF_RHS_XOR, rand_a[23:4] ^ (~rand_b[24:5] ^ rand_a[39:20]));
`signal(REPLACE_COND_CONST_ONE_ZERO, rand_a[0] ? 8'b1 : 8'b0);
`signal(REPLACE_COND_CONST_ZERO_ONE, rand_a[0] ? 8'b0 : 8'b1);
`signal(REPLACE_COND_CONST_ONES_ZERO, rand_a[0] ? 8'hff : 8'b0);
`signal(REPLACE_COND_CONST_ZERO_ONAE, rand_a[0] ? 8'b0 : 8'hff);
`signal(REPLACE_COND_CONST_ONE_ZERO, rand_a[0] ? 80'b1 : 80'b0);
`signal(REPLACE_COND_CONST_ZERO_ONE, rand_a[0] ? 80'b0 : 80'b1);
`signal(REPLACE_COND_CONST_ONES_ZERO, rand_a[0] ? -80'b1 : 80'b0);
`signal(REPLACE_COND_CONST_ZERO_ONAE, rand_a[0] ? 80'b0 : -80'b1);
`signal(REPLACE_COND_CAT_LHS_CONST_ONE_ZERO, rand_a[0] ? {8'b1, rand_b[0]} : {8'b0, rand_b[1]});
`signal(REPLACE_COND_CAT_LHS_CONST_ZERO_ONE, rand_a[0] ? {8'b0, rand_b[0]} : {8'b1, rand_b[1]});
`signal(REPLACE_COND_SAME_CAT_LHS, rand_a[0] ? {8'd0, rand_b[0]} : {8'd0, rand_b[1]});
@ -325,6 +325,7 @@ module t (
`signal(REMOVE_EQ_BIT_1, 1'b1 == rand_a[0]);
`signal(REMOVE_NEQ_BIT_0, 1'b0 != rand_a[0]);
`signal(REPLACE_NEQ_BIT_1, 1'b1 != rand_a[0]);
`signal(REPLACE_COND_INSERT, rand_a[0] ? {rand_b[63:40], {1'd0, rand_b[38:0]}} : rand_b);
// Operators that should work wiht mismatched widths
`signal(MISMATCHED_ShiftL,const_a << 4'd2);

View File

@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 41)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 38)
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes()

View File

@ -0,0 +1,10 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
//*************************************************************************
#include "Vt_opt_dead__Dpi.h"
void dpii_Keep() {}

View File

@ -11,13 +11,16 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.compile(verilator_flags2=[test.pli_filename])
test.execute()
# bug2227, Verilator unsupported, class dead
# This is what we really want:
# test.file_grep_not(test.obj_dir + "/V"+test.name+"__Syms.h", r'dead')
test.file_grep(test.obj_dir + "/V" + test.name + "__Syms.h", r'dead')
# test.file_grep_not(test.obj_dir + "/V"+test.name+"__Syms.h", r'Dead')
# test.file_grep_not(test.obj_dir + "/V" + test.name + "_classes.mk", r'Dead');
test.file_grep(test.obj_dir + "/V" + test.name + "__Dpi.h", r'dpii_Keep')
test.file_grep(test.obj_dir + "/V" + test.name + "__Dpi.h", r'dpix_Keep')
test.file_grep(test.obj_dir + "/V" + test.name + "_Pkg_public_kpt.h", r'public_int_Keep')
test.passes()

View File

@ -4,26 +4,95 @@
// SPDX-FileCopyrightText: 2020 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
class EmptyClass_Dead;
// Tests look for magic string "Dead" not to exist; V3Dead should remove these
class BaseClass_Dead;
endclass
class EmptyClass_Dead extends BaseClass_Dead;
endclass
package Pack_Dead;
typedef bit typedef_Dead;
endpackage
module Mod_Dead;
typedef class ModClass_Co_Dead;
class ModClass_Dead;
int memberb_dead;
int m_b_Dead;
ModClass_Co_Dead m_co_dead;
task method_Dead;
endtask
static task method_static_Dead;
endtask
endclass
class ModClass_Co_Dead;
ModClass_Dead m_dead; // Check that ModClass_Dead<->ModClass_Co_Dead link gets Deadified
endclass
endmodule
//TODO dead check with class extends
module Mod_Empty_Dead;
endmodule
module t;
module Mod_Parent_Empty_Dead;
Mod_Empty_Dead sub ();
endmodule
interface If_Dead;
function void if_func_Dead;
endfunction
modport modport_Dead(import if_func_Dead);
endinterface
package Pkg_public_kpt;
parameter int public_int_Keep /*verilator public_flat_rd*/ = 5;
endpackage
package Pkg_Keep;
import "DPI-C" function void dpii_Keep();
export "DPI-C" function dpix_Keep;
function void dpix_Keep;
endfunction
endpackage
module t ( /*AUTOARG*/);
typedef struct {int struct_member_Dead;} struct_Dead_t;
struct_Dead_t var_struct_Dead;
typedef int typedef_Dead1_t;
typedef typedef_Dead1_t typedef_Dead2_t;
function void func_Dead;
endfunction
generate
if (0) begin
Mod_Dead cell_dead ();
Mod_Dead cell_nogen_Dead ();
If_Dead if_nogen_Dead ();
end
endgenerate
Mod_Empty_Dead cell_empty_Dead ();
Mod_Parent_Empty_Dead cell_parent_empty_Dead ();
typedef_Dead1_t assigned_to_Dead1;
typedef_Dead2_t assigned_to_Dead2;
typedef_Dead2_t assigned_to_Dead3;
typedef_Dead2_t assigned_to_Dead4;
typedef_Dead2_t assigned_to_Dead5;
typedef_Dead2_t assigned_to_Dead6;
always_comb assigned_to_Dead6 = assigned_to_Dead5;
always_comb assigned_to_Dead5 = assigned_to_Dead4;
always_comb assigned_to_Dead4 = assigned_to_Dead3;
always_comb assigned_to_Dead3 = assigned_to_Dead2;
always_comb assigned_to_Dead2 = assigned_to_Dead1;
initial begin
assigned_to_Dead1 = 1;
assigned_to_Dead1 = 2;
$write("*-* All Finished *-*\n");
$finish;
end

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(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,50 @@
// 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
// Method/member access on a value-parameter whose type is
// the enclosing module's type-parameter.
typedef struct packed {
logic [7:0] S_TIMER;
logic [7:0] M_TIMER;
logic [7:0] M_EXT;
} my_irq_t;
module leaf #(
parameter type interrupts_t = logic,
parameter interrupts_t INTERRUPTS = '0
) ();
logic [7:0] observed;
always_comb observed = INTERRUPTS.M_TIMER;
endmodule
module mid #(
parameter type interrupts_t = logic,
parameter interrupts_t INTERRUPTS = '0
) ();
leaf #(
.interrupts_t(interrupts_t),
.INTERRUPTS(INTERRUPTS)
) l ();
endmodule
module t;
localparam type irq_t = my_irq_t;
localparam irq_t IRQ = '{S_TIMER: 8'hAA, M_TIMER: 8'h55, M_EXT: 8'hCC};
mid #(
.interrupts_t(irq_t),
.INTERRUPTS(IRQ)
) m ();
initial begin
#1;
if (m.l.observed !== 8'h55) begin
$write("%%Error: observed=%h expected 55\n", m.l.observed);
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

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(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,34 @@
// 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 checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
package pkg;
typedef struct packed {logic [1:0][31:0] bar;} T;
localparam T t = 64'h87654321_deadbeef;
endpackage
module foo #(
parameter type T = int,
parameter T t = 0
) ();
initial begin
`checkh(t.bar[0], 32'hdeadbeef);
`checkh(t.bar[1], 32'h87654321);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module top;
foo #(
.T(pkg::T),
.t(pkg::t)
) u_foo ();
endmodule