SystemVerilog: support product() with expressions on arrays

Enable queue/darray product() with (expr) by lowering to a dedicated _with sfunc, evaluating the expression per item, and reducing through a new %queue/product/obj/v runtime opcode. Add regressions and docs for queue and dynamic-array product_with behavior.

Made-with: Cursor
This commit is contained in:
mjoekhan 2026-04-28 23:48:59 +05:00
parent 03512faee6
commit 701c5f8a07
11 changed files with 143 additions and 23 deletions

View File

@ -106,7 +106,7 @@ static NetExpr* elab_queue_locator_with_predicate(
index_net->local_flag(true);
NetExpr* pred = 0;
if (method_suffix == "sum")
if (method_suffix == "sum" || method_suffix == "product")
pred = elab_and_eval(des, ws, with_expr, (int)ew, false, false,
ivl_type_base(element_type));
else
@ -138,6 +138,8 @@ static NetExpr* elab_queue_locator_with_predicate(
sfunc_name = lex_strings.make("$ivl_queue_method$max_with");
else if (method_suffix == "sum")
sfunc_name = lex_strings.make("$ivl_queue_method$sum_with");
else if (method_suffix == "product")
sfunc_name = lex_strings.make("$ivl_queue_method$product_with");
else
ivl_assert(loc, 0);
@ -3901,10 +3903,16 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
des->errors += 1;
}
if (with_expr_) {
cerr << get_fileline() << ": sorry: queue product() with a "
<< "`with` clause is not yet supported." << endl;
des->errors += 1;
return 0;
if (!parms_.empty()) {
cerr << get_fileline() << ": error: array locator "
<< "`with` clause cannot be combined with a "
<< "method argument." << endl;
des->errors += 1;
return 0;
}
return elab_queue_locator_with_predicate(
des, scope, *this, with_expr_, prop,
element_type, element_type, method_name);
}
if (!queue_method_element_is_integral_vec4(element_type)) {
cerr << get_fileline() << ": sorry: queue product() for this "
@ -4465,10 +4473,16 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
des->errors += 1;
}
if (with_expr_) {
cerr << get_fileline() << ": sorry: array product() with a "
<< "`with` clause is not yet supported." << endl;
des->errors += 1;
return 0;
if (!parms_.empty()) {
cerr << get_fileline() << ": error: array locator "
<< "`with` clause cannot be combined with a "
<< "method argument." << endl;
des->errors += 1;
return 0;
}
return elab_queue_locator_with_predicate(
des, scope, *this, with_expr_, prop,
element_type, element_type, method_name);
}
if (!queue_method_element_is_integral_vec4(element_type)) {
cerr << get_fileline() << ": sorry: array product() for this "
@ -4895,10 +4909,15 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
des->errors += 1;
}
if (with_expr_) {
cerr << get_fileline() << ": sorry: dynamic array product() with a "
<< "`with` clause is not yet supported." << endl;
des->errors += 1;
return 0;
if (!parms_.empty()) {
cerr << get_fileline() << ": error: array locator `with` clause "
"cannot be combined with a method argument." << endl;
des->errors += 1;
return 0;
}
return elab_queue_locator_with_predicate(
des, scope, *this, with_expr_, sub_expr, element_type,
element_type, method_name);
}
if (!queue_method_element_is_integral_vec4(element_type)) {
cerr << get_fileline() << ": sorry: dynamic array product() for this "
@ -5096,10 +5115,15 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
des->errors += 1;
}
if (with_expr_) {
cerr << get_fileline() << ": sorry: queue product() with a "
<< "`with` clause is not yet supported." << endl;
des->errors += 1;
return 0;
if (!parms_.empty()) {
cerr << get_fileline() << ": error: array locator `with` clause "
"cannot be combined with a method argument." << endl;
des->errors += 1;
return 0;
}
return elab_queue_locator_with_predicate(
des, scope, *this, with_expr_, sub_expr, element_type,
element_type, method_name);
}
if (!queue_method_element_is_integral_vec4(element_type)) {
cerr << get_fileline() << ": sorry: queue product() for this "

View File

@ -27,6 +27,7 @@ Behavior notes (LRM-oriented):
* product() returns the scalar reduction product of elements (integral
vector types).
`product() with (expr)` reduces the expression result for each item.
* For dynamic arrays, runtime support treats storage as vvp_darray (including
atom-backed integral arrays), not only vvp_queue_vec4. See vvp/vthread.cc:
@ -59,6 +60,8 @@ Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list):
sv_class_queue_prop_locators.v locator methods on class queue properties.
sv_queue_product.v integral product() reduction on queues.
sv_darray_product.v integral product() reduction on dynamic arrays.
sv_queue_product_with.v product() with expression on queues.
sv_darray_product_with.v product() with expression on dynamic arrays.
sv_queue_sum.v integral sum() reduction on queues.
sv_darray_sum.v integral sum() reduction on dynamic arrays.
sv_queue_sum_with.v sum() with expression on queues.

View File

@ -0,0 +1,24 @@
// Regression: dynamic array product() reduction with expression.
module top;
bit failed = 0;
`define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end
int a[];
int p;
initial begin
a = '{10, -3, 5};
p = a.product() with (item > 0);
`CHK(p === 0);
p = a.product() with (item + 1);
`CHK(p === -132);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,24 @@
// Regression: queue product() reduction with expression.
module top;
bit failed = 0;
`define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end
int q[$];
int p;
initial begin
q = '{4, 7, 2};
p = q.product() with (item > 3);
`CHK(p === 0);
p = q.product() with (item + 1);
`CHK(p === 120);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -258,6 +258,7 @@ sv_darray_find_locators vvp_tests/sv_darray_find_locators.json
sv_darray_min_max vvp_tests/sv_darray_min_max.json
sv_darray_min_max_with vvp_tests/sv_darray_min_max_with.json
sv_darray_product vvp_tests/sv_darray_product.json
sv_darray_product_with vvp_tests/sv_darray_product_with.json
sv_darray_sum vvp_tests/sv_darray_sum.json
sv_darray_sum_with vvp_tests/sv_darray_sum_with.json
sv_darray_unique vvp_tests/sv_darray_unique.json
@ -288,6 +289,7 @@ sv_queue_find_with vvp_tests/sv_queue_find_with.json
sv_queue_min_max vvp_tests/sv_queue_min_max.json
sv_queue_min_max_with vvp_tests/sv_queue_min_max_with.json
sv_queue_product vvp_tests/sv_queue_product.json
sv_queue_product_with vvp_tests/sv_queue_product_with.json
sv_queue_sum vvp_tests/sv_queue_sum.json
sv_queue_sum_with vvp_tests/sv_queue_sum_with.json
sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_darray_product_with.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog dynamic array product() with expression",
"type" : "CE"
}
}

View File

@ -0,0 +1,9 @@
{
"type" : "normal",
"source" : "sv_queue_product_with.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog queue product() with expression",
"type" : "CE"
}
}

View File

@ -469,6 +469,8 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
mode = 9;
else if (strcmp(name, "$ivl_queue_method$sum_with") == 0)
mode = 10;
else if (strcmp(name, "$ivl_queue_method$product_with") == 0)
mode = 11;
else
return 1;
@ -489,7 +491,7 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%prop/queue/size %u;\n", pidx);
fprintf(vvp_out, " %%ix/vec4/s %u;\n", n_reg);
if (mode == 0 || mode == 1 || mode == 6 || mode == 7 ||
mode == 8 || mode == 9 || mode == 10) {
mode == 8 || mode == 9 || mode == 10 || mode == 11) {
fprintf(vvp_out, " %%queue/new_empty/v;\n");
}
if (!reverse) {
@ -520,7 +522,7 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%push/ix/vec4 %u, 32, 1;\n", i_reg);
fprintf(vvp_out, " %%store/vec4 v%p_0, 0, 32;\n", idx_sig);
if (mode == 10) {
if (mode == 10 || mode == 11) {
draw_eval_vec4(pred);
fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid);
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
@ -551,7 +553,7 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%push/ix/vec4 %u, 32, 1;\n", i_reg);
fprintf(vvp_out, " %%queue/append_word/v 32;\n");
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
} else if (mode == 10) {
} else if (mode == 10 || mode == 11) {
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
} else if (mode == 2) {
fprintf(vvp_out, " %%queue/new_empty/v;\n");
@ -597,6 +599,9 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
} else if (mode == 10) {
fprintf(vvp_out, " %%queue/sum/obj/v %u;\n", elem_wid);
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
} else if (mode == 11) {
fprintf(vvp_out, " %%queue/product/obj/v %u;\n", elem_wid);
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
} else if (mode == 8 || mode == 9) {
fprintf(vvp_out, " %%queue/unique/obj/v %u;\n",
mode == 9 ? 32 : elem_wid);
@ -615,7 +620,7 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%queue/size/v v%p_0;\n", sig);
fprintf(vvp_out, " %%ix/vec4/s %u;\n", n_reg);
if (mode == 0 || mode == 1 || mode == 6 || mode == 7 ||
mode == 8 || mode == 9 || mode == 10) {
mode == 8 || mode == 9 || mode == 10 || mode == 11) {
fprintf(vvp_out, " %%queue/new_empty/v;\n");
}
if (!reverse) {
@ -646,7 +651,7 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%push/ix/vec4 %u, 32, 1;\n", i_reg);
fprintf(vvp_out, " %%store/vec4 v%p_0, 0, 32;\n", idx_sig);
if (mode == 10) {
if (mode == 10 || mode == 11) {
draw_eval_vec4(pred);
fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid);
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
@ -677,7 +682,7 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
fprintf(vvp_out, " %%push/ix/vec4 %u, 32, 1;\n", i_reg);
fprintf(vvp_out, " %%queue/append_word/v 32;\n");
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
} else if (mode == 10) {
} else if (mode == 10 || mode == 11) {
fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom);
} else if (mode == 2) {
fprintf(vvp_out, " %%queue/new_empty/v;\n");
@ -714,6 +719,8 @@ static int eval_queue_method_find_with(ivl_expr_t expr)
lab_loop_end);
if (mode == 10) {
fprintf(vvp_out, " %%queue/sum/obj/v %u;\n", elem_wid);
} else if (mode == 11) {
fprintf(vvp_out, " %%queue/product/obj/v %u;\n", elem_wid);
} else if (mode == 8 || mode == 9) {
fprintf(vvp_out, " %%queue/unique/obj/v %u;\n",
mode == 9 ? 32 : elem_wid);

View File

@ -252,6 +252,7 @@ extern bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_APPEND_WORD_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_NEW_EMPTY_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_PRODUCT_PROP_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_PRODUCT_OBJ_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_PRODUCT_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_SIZE_V(vthread_t thr, vvp_code_t code);
extern bool of_QUEUE_SUM_OBJ_V(vthread_t thr, vvp_code_t code);

View File

@ -298,6 +298,7 @@ static const struct opcode_table_s opcode_table[] = {
{ "%queue/min/prop/v", of_QUEUE_MIN_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} },
{ "%queue/min/v", of_QUEUE_MIN_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%queue/new_empty/v", of_QUEUE_NEW_EMPTY_V, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%queue/product/obj/v", of_QUEUE_PRODUCT_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} },
{ "%queue/product/prop/v", of_QUEUE_PRODUCT_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} },
{ "%queue/product/v", of_QUEUE_PRODUCT_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%queue/size/v", of_QUEUE_SIZE_V, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },

View File

@ -6481,6 +6481,22 @@ bool of_QUEUE_PRODUCT_PROP_V(vthread_t thr, vvp_code_t cp)
return true;
}
bool of_QUEUE_PRODUCT_OBJ_V(vthread_t thr, vvp_code_t cp)
{
unsigned wid = cp->bit_idx[0];
vvp_object_t src_obj;
thr->pop_object(src_obj);
vvp_queue_vec4* qsrc = 0;
vvp_darray* dsrc = 0;
get_queue_or_darray_vec4_from_object(src_obj, qsrc, dsrc);
vvp_vector4_t prod =
qsrc ? queue_product_words_src(qsrc, wid)
: queue_product_words_src(dsrc, wid);
thr->push_vec4(prod);
return true;
}
bool of_QUEUE_SUM_V(vthread_t thr, vvp_code_t cp)
{
vvp_net_t* net = cp->net;