Support assoc array methods with wide value types (#7680)

This commit is contained in:
pawelktk 2026-06-10 15:39:43 +02:00 committed by GitHub
parent d1319cf81e
commit 75993ca9ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 261 additions and 8 deletions

View File

@ -97,6 +97,28 @@ struct VlWide final {
bool operator!=(const VlWide<N_Words>& that) const VL_PURE { return !(*this == that); }
EData& operator[](size_t index) VL_MT_SAFE { return m_storage[index]; }
const EData& operator[](size_t index) const VL_MT_SAFE { return m_storage[index]; }
VlWide<N_Words>& operator&=(const VlWide& rhs) {
VL_AND_W(N_Words, *this, *this, rhs);
return *this;
}
VlWide<N_Words>& operator|=(const VlWide& rhs) {
VL_OR_W(N_Words, *this, *this, rhs);
return *this;
}
VlWide<N_Words>& operator^=(const VlWide& rhs) {
VL_XOR_W(N_Words, *this, *this, rhs);
return *this;
}
VlWide<N_Words>& operator+=(const VlWide& rhs) {
VL_ADD_W(N_Words, *this, *this, rhs);
return *this;
}
VlWide<N_Words>& operator*=(const VlWide& rhs) {
VlWide<N_Words> out{};
VL_MUL_W(N_Words, out, *this, rhs);
for (size_t i = 0; i < N_Words; ++i) m_storage[i] = out.m_storage[i];
return *this;
}
// METHODS
EData& at(size_t index) VL_MT_SAFE { return m_storage[index]; }
@ -1280,7 +1302,7 @@ public:
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
T_Value out = T_Value{};
for (const auto& i : m_map) out += i.second;
return out;
}
@ -1291,8 +1313,9 @@ public:
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0); // The big three do it this way
T_Value out = T_Value(1);
// The big three return 0 when assoc array is empty
if (m_map.empty()) return T_Value{};
T_Value out = T_Value{1};
for (const auto& i : m_map) out *= i.second;
return out;
}
@ -1304,8 +1327,9 @@ public:
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0); // The big three do it this way
T_Value out = ~T_Value(0);
// The big three return 0 when assoc array is empty
if (m_map.empty()) return T_Value{};
T_Value out = m_map.cbegin()->second;
for (const auto& i : m_map) out &= i.second;
return out;
}
@ -1317,7 +1341,7 @@ public:
return out;
}
T_Value r_or() const {
T_Value out = T_Value(0);
T_Value out = T_Value{};
for (const auto& i : m_map) out |= i.second;
return out;
}
@ -1328,7 +1352,7 @@ public:
return out;
}
T_Value r_xor() const {
T_Value out = T_Value(0);
T_Value out = T_Value{};
for (const auto& i : m_map) out ^= i.second;
return out;
}

View File

@ -4352,6 +4352,12 @@ class WidthVisitor final : public VNVisitor {
}
void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
AstCMethodHard* newp = nullptr;
if (nodep->withp() && adtypep->subDTypep()->isWide()) {
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: `with` clause on assoc arrays with wide value types in method '"
<< nodep->prettyName() << "'");
}
if (nodep->name() == "num" // function int num()
|| nodep->name() == "size") {
methodOkArguments(nodep, 0, 0);

View File

@ -22,15 +22,23 @@ module t;
int qe[int]; // Empty
int qv[$]; // Value returns
int qi[$]; // Index returns
bit[229:0] qw[int]; // Wide values
bit[229:0] qwe[int]; // Wide values - empty
bit[229:0] qwv[$]; // Wide values - Value returns
int qwi[$]; // Wide values - Index returns
point points_q[int];
point points_qe[int]; // Empty points
point points_qv[$];
int i;
bit b;
bit[229:0] w;
q = '{10: 1, 11: 2, 12: 2, 13: 4, 14: 3};
`checkp(q, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3}");
qw = '{10: 1, 11: 2, 12: 2, 13: 4, 14: 3};
`checkp(qw, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3}");
// NOT tested: with ... selectors
//q.sort; // Not legal on assoc - see t_assoc_meth_bad
@ -43,16 +51,34 @@ module t;
`checkp(qv, "'{'h1, 'h2, 'h4, 'h3}");
qv = qe.unique;
`checkp(qv, "'{}");
qwv = qw.unique;
`checkp(qwv, "'{'h1, 'h2, 'h4, 'h3}");
qwv = qwe.unique;
`checkp(qwv, "'{}");
qi = q.unique_index;
qi.sort;
`checkp(qi, "'{'ha, 'hb, 'hd, 'he}");
qi = qe.unique_index;
`checkp(qi, "'{}");
qwi = qw.unique_index;
qwi.sort;
`checkp(qwi, "'{'ha, 'hb, 'hd, 'he}");
qwi = qwe.unique_index;
`checkp(qwi, "'{}");
points_q[0] = point'{1, 2};
points_q[1] = point'{2, 4};
points_q[5] = point'{1, 4};
qi = points_qe.unique_index();
`checkp(qi, "'{}");
qi = points_q.unique_index();
`checkh(qi.size, 3);
points_qv = points_q.unique(p) with (p.x);
`checkh(points_qv.size, 2);
qi = points_q.unique_index (p) with (p.x + p.y);
@ -113,6 +139,10 @@ module t;
qv = q.min;
`checkp(qv, "'{'h1}");
qwv = qw.min;
`checkp(qwv, "'{'h1}");
points_qv = points_q.min(p) with (p.x + p.y);
if (points_qv[0].x != 1 || points_qv[0].y != 2) $stop;
@ -130,8 +160,13 @@ module t;
qv = qe.max(x) with (x + 1);
`checkp(qv, "'{}");
// Reduction methods
// Wide
qwv = qwe.min;
`checkp(qwv, "'{}");
qwv = qwe.max;
`checkp(qwv, "'{}");
// Reduction methods
i = q.sum;
`checkh(i, 32'hc);
i = q.sum with (item + 1);
@ -141,6 +176,12 @@ module t;
i = q.product with (item + 1);
`checkh(i, 32'h168);
// Wide
w = qw.sum;
`checkh(w, 230'hc);
w = qw.product;
`checkh(w, 230'h30);
i = qe.sum;
`checkh(i, 32'h0);
i = qe.sum with (item + 1);
@ -150,6 +191,12 @@ module t;
i = qe.product with (item + 1);
`checkh(i, 32'h0);
// Wide
w = qwe.sum;
`checkh(w, 230'h0);
w = qwe.product;
`checkh(w, 230'h0);
q = '{10: 32'b1100, 11: 32'b1010};
i = q.and;
`checkh(i, 32'b1000);
@ -164,6 +211,14 @@ module t;
i = q.xor with (item + 1);
`checkh(i, 32'b0110);
qw = '{10: 230'b1100, 11: 230'b1010};
w = qw.and;
`checkh(w, 230'b1000);
w = qw.or;
`checkh(w, 230'b1110);
w = qw.xor;
`checkh(w, 230'b0110);
i = qe.and;
`checkh(i, 32'b0);
i = qe.and with (item + 1);
@ -177,6 +232,14 @@ module t;
i = qe.xor with (item + 1);
`checkh(i, 32'b0);
// Wide
w = qwe.and;
`checkh(w, 230'b0);
w = qwe.or;
`checkh(w, 230'b0);
w = qwe.xor;
`checkh(w, 230'b0);
i = q.and();
`checkh(i, 32'b1000);
i = q.and() with (item + 1);
@ -190,6 +253,14 @@ module t;
i = q.xor() with (item + 1);
`checkh(i, 32'b0110);
// Wide
w = qw.and();
`checkh(w, 230'b1000);
w = qw.or();
`checkh(w, 230'b1110);
w = qw.xor();
`checkh(w, 230'b0110);
i = qe.and();
`checkh(i, 32'b0);
i = qe.or();
@ -197,6 +268,14 @@ module t;
i = qe.xor();
`checkh(i, 32'b0);
// Wide
w = qwe.and();
`checkh(w, 230'b0);
w = qwe.or();
`checkh(w, 230'b0);
w = qwe.xor();
`checkh(w, 230'b0);
q = '{10: 1, 11: 2};
qe = '{10: 1, 11: 2};
`checkh(q == qe, 1'b1);

View File

@ -0,0 +1,78 @@
%Error-UNSUPPORTED: t/t_assoc_unsup.v:17:15: Unsupported: `with` clause on assoc arrays with wide value types in method 'min'
: ... note: In instance 't'
17 | qwv = qwe.min(x) with (x + 1);
| ^~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_assoc_unsup.v:18:15: Unsupported: `with` clause on assoc arrays with wide value types in method 'max'
: ... note: In instance 't'
18 | qwv = qwe.max(x) with (x + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:20:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'sum'
: ... note: In instance 't'
20 | w = qw.sum with (item + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:21:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'product'
: ... note: In instance 't'
21 | w = qw.product with (item + 1);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:23:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'sum'
: ... note: In instance 't'
23 | w = qwe.sum with (item + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:24:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'product'
: ... note: In instance 't'
24 | w = qwe.product with (item + 1);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:27:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'and'
: ... note: In instance 't'
27 | w = qw.and with (item + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:28:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'or'
: ... note: In instance 't'
28 | w = qw.or with (item + 1);
| ^~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:29:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'xor'
: ... note: In instance 't'
29 | w = qw.xor with (item + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:31:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'and'
: ... note: In instance 't'
31 | w = qw.and() with (item + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:32:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'or'
: ... note: In instance 't'
32 | w = qw.or() with (item + 1);
| ^~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:33:12: Unsupported: `with` clause on assoc arrays with wide value types in method 'xor'
: ... note: In instance 't'
33 | w = qw.xor() with (item + 1);
| ^~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:35:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'find'
: ... note: In instance 't'
35 | qwv = qw.find with (item == 2);
| ^~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:36:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_first'
: ... note: In instance 't'
36 | qwv = qw.find_first with (item == 2);
| ^~~~~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:37:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_last'
: ... note: In instance 't'
37 | qwv = qw.find_last with (item == 2);
| ^~~~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:39:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_index'
: ... note: In instance 't'
39 | qi = qw.find_index with (item == 2);
| ^~~~~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:40:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_first_index'
: ... note: In instance 't'
40 | qi = qw.find_first_index with (item == 2);
| ^~~~~~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:41:13: Unsupported: `with` clause on assoc arrays with wide value types in method 'find_last_index'
: ... note: In instance 't'
41 | qi = qw.find_last_index with (item == 2);
| ^~~~~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_assoc_unsup.v:45:14: Unsupported: `with` clause on assoc arrays with wide value types in method 'map'
: ... note: In instance 't'
45 | qwv = qw.map(el) with (el / 100);
| ^~~
%Error: Exiting due to

16
test_regress/t/t_assoc_unsup.py Executable file
View File

@ -0,0 +1,16 @@
#!/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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("linter")
test.lint(fails=True, expect_filename=test.golden_filename)
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 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t;
initial begin
bit[229:0] qw[int]; // Wide values
bit[229:0] qwe[int]; // Wide values - empty
bit[229:0] qwv[$]; // Wide values - Value returns
int qi[$]; // Index returns
bit[229:0] w;
qw = '{10: 1, 11: 2, 12: 2, 13: 4, 14: 3};
qwv = qwe.min(x) with (x + 1);
qwv = qwe.max(x) with (x + 1);
w = qw.sum with (item + 1);
w = qw.product with (item + 1);
w = qwe.sum with (item + 1);
w = qwe.product with (item + 1);
qw = '{10: 230'b1100, 11: 230'b1010};
w = qw.and with (item + 1);
w = qw.or with (item + 1);
w = qw.xor with (item + 1);
w = qw.and() with (item + 1);
w = qw.or() with (item + 1);
w = qw.xor() with (item + 1);
qwv = qw.find with (item == 2);
qwv = qw.find_first with (item == 2);
qwv = qw.find_last with (item == 2);
qi = qw.find_index with (item == 2);
qi = qw.find_first_index with (item == 2);
qi = qw.find_last_index with (item == 2);
// Map method (IEEE 1800-2023 7.12.5)
qw = '{1: 100, 2: 200, 3: 300};
qwv = qw.map(el) with (el / 100);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule