diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 3ce2f1087..d379a27a5 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2077,20 +2077,41 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); return true; } - } else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isPure()) { + } else if (m_doV && VN_IS(nodep->lhsp(), Concat)) { bool need_temp = false; - if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS? + bool need_temp_pure = !nodep->rhsp()->isPure(); + if (m_warn && !VN_IS(nodep, AssignDly) + && !need_temp_pure) { // Is same var on LHS and RHS? // Note only do this (need user4) when m_warn, which is // done as unique visitor + // If the rhs is not pure, we need a temporary variable anyway const VNUser4InUse m_inuser4; nodep->lhsp()->foreach([](const AstVarRef* nodep) { - if (nodep->varp()) nodep->varp()->user4(1); + UASSERT_OBJ(nodep->varp(), nodep, "Unlinked VarRef"); + nodep->varp()->user4(1); }); nodep->rhsp()->foreach([&need_temp](const AstVarRef* nodep) { - if (nodep->varp() && nodep->varp()->user4()) need_temp = true; + UASSERT_OBJ(nodep->varp(), nodep, "Unlinked VarRef"); + if (nodep->varp()->user4()) need_temp = true; }); } - if (need_temp) { + if (need_temp_pure) { + // if the RHS is impure we need to create a temporary variable for it, because + // further handling involves copying of the RHS. + UINFO(4, " ASSITEMPPURE " << nodep << endl); + // ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(temp,rhs), + // ASSIGN(lc1,SEL(temp,{size1})), + // ASSIGN(lc2,SEL(temp,{size2})) + + AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); + AstVar* const tempPurep = new AstVar{rhsp->fileline(), VVarType::BLOCKTEMP, + m_concswapNames.get(rhsp), rhsp->dtypep()}; + m_modp->addStmtsp(tempPurep); + AstNodeAssign* const asnp = nodep->cloneType( + new AstVarRef{rhsp->fileline(), tempPurep, VAccess::WRITE}, rhsp); + nodep->addHereThisAsNext(asnp); + nodep->rhsp(new AstVarRef{rhsp->fileline(), tempPurep, VAccess::READ}); + } else if (need_temp) { // The first time we constify, there may be the same variable on the LHS // and RHS. In that case, we must use temporaries, or {a,b}={b,a} will break. UINFO(4, " ASSITEMP " << nodep << endl); diff --git a/test_regress/t/t_concat_impure.py b/test_regress/t/t_concat_impure.py new file mode 100755 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_concat_impure.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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.execute() + +test.passes() diff --git a/test_regress/t/t_concat_impure.v b/test_regress/t/t_concat_impure.v new file mode 100644 index 000000000..64bb7e0bd --- /dev/null +++ b/test_regress/t/t_concat_impure.v @@ -0,0 +1,27 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +int global_variable = 0; + +function int side_effect; + global_variable++; + return 1; +endfunction + +module t (/*AUTOARG*/); + reg [15:0] x; + reg [15:0] y; + initial begin + {x, y} = side_effect() + 2; + + if (y != 3) $stop; + if (x != 0) $stop; + if (global_variable != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule