From 316fb02c60c543268824b2ed443fc584cb354095 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Tue, 17 Mar 2026 19:36:54 +0100 Subject: [PATCH] Support assignment patterns as comparison operands (#7269) * Support assignment patterns as EQ/NEQ comparison operands * Apply 'make format' * update the .out file * add both-sides case --------- Co-authored-by: github action --- src/V3Width.cpp | 18 ++- test_regress/t/t_assign_pattern_cmp.py | 18 +++ test_regress/t/t_assign_pattern_cmp.v | 166 +++++++++++++++++++++++++ test_regress/t/t_stream_unpack_lhs.out | 27 +--- 4 files changed, 205 insertions(+), 24 deletions(-) create mode 100755 test_regress/t/t_assign_pattern_cmp.py create mode 100644 test_regress/t/t_assign_pattern_cmp.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index d88293db6..e0ada32d0 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -7545,8 +7545,22 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); assertAtExpr(nodep); if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); - userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + // Assignment patterns cannot self-determine their type. + // Iterate the non-pattern operand first so its dtype is available, + // then propagate that dtype to the pattern operand (analogous to + // how assignments pass LHS dtype to RHS patterns). + const bool lhsIsPat = VN_IS(nodep->lhsp(), Pattern); + const bool rhsIsPat = VN_IS(nodep->rhsp(), Pattern); + if (lhsIsPat && !rhsIsPat) { + userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + userIterateAndNext(nodep->lhsp(), WidthVP{nodep->rhsp()->dtypep(), PRELIM}.p()); + } else if (rhsIsPat && !lhsIsPat) { + userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{nodep->lhsp()->dtypep(), PRELIM}.p()); + } else { + userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + } const bool isAggrLhs = isAggregateType(nodep->lhsp()); const bool isAggrRhs = isAggregateType(nodep->rhsp()); diff --git a/test_regress/t/t_assign_pattern_cmp.py b/test_regress/t/t_assign_pattern_cmp.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_assign_pattern_cmp.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_assign_pattern_cmp.v b/test_regress/t/t_assign_pattern_cmp.v new file mode 100644 index 000000000..7ae70c3ae --- /dev/null +++ b/test_regress/t/t_assign_pattern_cmp.v @@ -0,0 +1,166 @@ +// DESCRIPTION: Verilator: Test assignment patterns as comparison operands +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +module t; + + // Fixed-size array comparison + int arr4[4]; + typedef int arr4_t[4]; + + // Queue comparison + int q[$]; + int sub[$]; + + // Struct comparison + typedef struct packed { + logic [7:0] a; + logic [7:0] b; + } pair_t; + + pair_t p1; + + // Nested: array of structs + pair_t parr[2]; + + // Different element types + logic [31:0] wide_arr[3]; + byte byte_arr[2]; + + initial begin + // ------------------------------------------------------- + // 1. Fixed-size array: EQ with assignment pattern + // ------------------------------------------------------- + arr4 = '{10, 20, 30, 40}; + if (arr4 == '{10, 20, 30, 40}) begin + // expected + end else begin + $display("FAIL: arr4 == pattern"); + $stop; + end + + // 2. Fixed-size array: NEQ with assignment pattern + if (arr4 != '{10, 20, 30, 99}) begin + // expected + end else begin + $display("FAIL: arr4 != pattern"); + $stop; + end + + // 3. Pattern on LHS of comparison + if ('{10, 20, 30, 40} == arr4) begin + // expected + end else begin + $display("FAIL: pattern == arr4 (LHS)"); + $stop; + end + + // 4. Pattern on LHS of NEQ + if ('{10, 20, 30, 99} != arr4) begin + // expected + end else begin + $display("FAIL: pattern != arr4 (LHS NEQ)"); + $stop; + end + + // ------------------------------------------------------- + // 5. Queue: EQ with assignment pattern + // ------------------------------------------------------- + q = '{10, 20, 30, 40, 50}; + sub = q[1:3]; + if (sub == '{20, 30, 40}) begin + // expected + end else begin + $display("FAIL: queue slice == pattern"); + $stop; + end + + // 6. Queue: NEQ with assignment pattern + if (sub != '{20, 30, 99}) begin + // expected + end else begin + $display("FAIL: queue slice != pattern"); + $stop; + end + + // ------------------------------------------------------- + // 7. Struct: EQ with assignment pattern + // ------------------------------------------------------- + p1 = '{a: 8'hAA, b: 8'h55}; + if (p1 == '{a: 8'hAA, b: 8'h55}) begin + // expected + end else begin + $display("FAIL: struct == pattern"); + $stop; + end + + // 8. Struct: NEQ with assignment pattern + if (p1 != '{a: 8'hAA, b: 8'hFF}) begin + // expected + end else begin + $display("FAIL: struct != pattern"); + $stop; + end + + // ------------------------------------------------------- + // 9. Array of structs: EQ with nested pattern + // ------------------------------------------------------- + parr[0] = '{a: 8'h01, b: 8'h02}; + parr[1] = '{a: 8'h03, b: 8'h04}; + if (parr == '{'{a: 8'h01, b: 8'h02}, '{a: 8'h03, b: 8'h04}}) begin + // expected + end else begin + $display("FAIL: array of structs == nested pattern"); + $stop; + end + + // ------------------------------------------------------- + // 10. Different element widths + // ------------------------------------------------------- + wide_arr = '{32'hDEAD_BEEF, 32'hCAFE_BABE, 32'h1234_5678}; + if (wide_arr == '{32'hDEAD_BEEF, 32'hCAFE_BABE, 32'h1234_5678}) begin + // expected + end else begin + $display("FAIL: wide array == pattern"); + $stop; + end + + byte_arr = '{8'hAB, 8'hCD}; + if (byte_arr == '{8'hAB, 8'hCD}) begin + // expected + end else begin + $display("FAIL: byte array == pattern"); + $stop; + end + + // ------------------------------------------------------- + // 11. Negative case: pattern that should not match + // ------------------------------------------------------- + if (arr4 == '{99, 99, 99, 99}) begin + $display("FAIL: arr4 should not match all-99 pattern"); + $stop; + end + + // ------------------------------------------------------- + // 12. Both sides are typed assignment patterns + // ------------------------------------------------------- + if (arr4_t'{10, 20, 30, 40} == arr4_t'{10, 20, 30, 40}) begin + // expected + end else begin + $display("FAIL: typed pattern == typed pattern"); + $stop; + end + + if (arr4_t'{10, 20, 30, 40} != arr4_t'{10, 20, 30, 99}) begin + // expected + end else begin + $display("FAIL: typed pattern != typed pattern"); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_stream_unpack_lhs.out b/test_regress/t/t_stream_unpack_lhs.out index b43bab2b8..2bd06441d 100644 --- a/test_regress/t/t_stream_unpack_lhs.out +++ b/test_regress/t/t_stream_unpack_lhs.out @@ -1,22 +1,5 @@ -%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:112:34: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ - : ... note: In instance 't' - 112 | if (unpacked_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:113:34: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ - : ... note: In instance 't' - 113 | if (unpacked_asc_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - | ^~ -%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:114:34: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ - : ... note: In instance 't' - 114 | if (unpacked_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; - | ^~ -%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:116:32: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ - : ... note: In instance 't' - 116 | if (packed_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - | ^~ -%Error: Internal Error: t/t_stream_unpack_lhs.v:116:32: ../V3Width.cpp:#: Node has no type - : ... note: In instance 't' - 116 | if (packed_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - | ^~ - ... This fatal error may be caused by the earlier error(s); resolve those first. +%Error: Internal Error: t/t_stream_unpack_lhs.v:58:85: ../V3Const.cpp:#: sWidth >= dWidth should have caused an error earlier + : ... note: In instance 't' + 58 | {>>{concat5_dout4, concat5_dout3, concat5_dout2, concat5_dout1, concat5_dout0}} = concat_din; + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.