diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index d05c9944e..53241ebdc 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -230,6 +230,7 @@ Risto Pejašinović Robert Balas Robin Heinemann Rodrigo Batista de Moraes +Rowan Goemans Rupert Swarbrick Ryan Ziegler Ryszard Rozak diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 81e622ee2..af5c06d65 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -105,8 +105,8 @@ class SliceVisitor final : public VNVisitor { newp = nullptr; int itemIdx = 0; int i = 0; + const AstInitArray::KeyItemMap& itemMap = initp->map(); if (const int prevItemIdx = initp->user2()) { - const AstInitArray::KeyItemMap& itemMap = initp->map(); const auto it = itemMap.find(considerOrder(arrayp, prevItemIdx)); if (it != itemMap.end()) { const AstInitItem* itemp = it->second; @@ -119,8 +119,10 @@ class SliceVisitor final : public VNVisitor { } const AstNodeDType* const expectedItemDTypep = arrayp->subDTypep()->skipRefp(); while (i <= elemIdx) { + const auto itemIt = itemMap.find(considerOrder(arrayp, itemIdx)); AstNodeExpr* const itemp - = initp->getIndexDefaultedValuep(considerOrder(arrayp, itemIdx)); + = itemIt != itemMap.end() ? itemIt->second->valuep() : initp->defaultp(); + const bool directItem = itemIt != itemMap.end(); if (!itemp && !m_assignError) { nodep->v3error("Array initialization has too few elements, need element " << elemIdx); @@ -132,7 +134,7 @@ class SliceVisitor final : public VNVisitor { = AstNode::computeCastable(expectedItemDTypep, itemRawDTypep, itemp); if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) { if (i == elemIdx) { - newp = itemp->cloneTreePure(false); + newp = itemp->cloneTree(false, !directItem && needPure); break; } else { // Check the next item ++i; @@ -184,7 +186,6 @@ class SliceVisitor final : public VNVisitor { m_assignError = true; } if (newp) { - const AstInitArray::KeyItemMap& itemMap = initp->map(); const auto it = itemMap.find(considerOrder(arrayp, itemIdx)); if (it != itemMap.end()) { // Remember current position for the next invocation. initp->user2(itemIdx); @@ -283,16 +284,6 @@ class SliceVisitor final : public VNVisitor { AstNodeAssign* const newp = nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx, elemIdx != 0), cloneAndSel(nodep->rhsp(), elements, elemIdx, elemIdx != 0)); - if (elemIdx == 0) { - nodep->foreach([this](AstExprStmt* const exprp) { - // Result expression is always evaluated to the same value, so the statements - // can be removed once they were included in the expression created for the 1st - // element. - AstNodeExpr* const resultp = exprp->resultp()->unlinkFrBack(); - exprp->replaceWith(resultp); - VL_DO_DANGLING(pushDeletep(exprp), exprp); - }); - } UINFOTREE(9, newp, "", "new"); newlistp = AstNode::addNext(newlistp, newp); } @@ -356,17 +347,6 @@ class SliceVisitor final : public VNVisitor { T_NodeBiop* const clonep = new T_NodeBiop{ nodep->fileline(), cloneAndSel(nodep->lhsp(), elements, elemIdx, elemIdx != 0), cloneAndSel(nodep->rhsp(), elements, elemIdx, elemIdx != 0)}; - if (elemIdx == 0) { - nodep->foreach([this](AstExprStmt* const exprp) { - // Result expression is always evaluated to the same value, so the - // statements can be removed once they were included in the expression - // created for the 1st element. - AstNodeExpr* const resultp = exprp->resultp()->unlinkFrBack(); - exprp->replaceWith(resultp); - VL_DO_DANGLING(pushDeletep(exprp), exprp); - }); - } - if (!logp) { logp = clonep; } else { diff --git a/test_regress/t/t_array_pattern_side_effect.py b/test_regress/t/t_array_pattern_side_effect.py new file mode 100644 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_array_pattern_side_effect.py @@ -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() diff --git a/test_regress/t/t_array_pattern_side_effect.v b/test_regress/t/t_array_pattern_side_effect.v new file mode 100644 index 000000000..451e6d8cc --- /dev/null +++ b/test_regress/t/t_array_pattern_side_effect.v @@ -0,0 +1,49 @@ +// DESCRIPTION: Verilator: Assignment pattern preserves array expression side effects +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-FileCopyrightText: 2026 Rowan Goemans +// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module t; + + // verilator lint_off ASCRANGE + typedef logic [0:2][7:0] triple_lv_t; + // verilator lint_on ASCRANGE + typedef triple_lv_t pair_t [0:1]; + + function automatic triple_lv_t mk3( + input logic [7:0] a, + input logic [7:0] b, + input logic [7:0] c + ); + mk3 = '{0: a, 1: b, 2: c}; + endfunction + + pair_t pair; + + initial begin + // verilator lint_off SIDEEFFECT + pair = '{ + 0: mk3(8'd1, 8'd2, 8'd3), + 1: mk3(8'd4, 8'd5, 8'd6) + }; + // verilator lint_on SIDEEFFECT + + `checkd(pair[0][0], 8'd1); + `checkd(pair[0][1], 8'd2); + `checkd(pair[0][2], 8'd3); + `checkd(pair[1][0], 8'd4); + `checkd(pair[1][1], 8'd5); + `checkd(pair[1][2], 8'd6); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule