SV: implement reverse() for queues and dynamic arrays

Lower a.reverse() and q.reverse() to $ivl_darray_method$reverse with new
VVP opcodes %reverse/obj and %reverse/prop/obj. Implement reverse_elems()
on vvp_darray and queue storage types; dispatch queues via peek<vvp_queue>
before vvp_darray so virtual overrides apply.

Includes regressions for standalone arrays/queues and class properties.
README_sv_queue_locators.txt documents behavior and tests.

Made-with: Cursor
This commit is contained in:
mjoekhan 2026-04-29 00:48:56 +05:00
parent 084db5ef73
commit e597156d4d
16 changed files with 297 additions and 11 deletions

View File

@ -3965,6 +3965,12 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
&netvector_t::atom2s32,
method_name, "$size");
}
if (method_name == "reverse") {
static const std::vector<perm_string> parm_names;
return elaborate_sys_task_method_(des, scope, prop, method_name,
"$ivl_darray_method$reverse",
parm_names);
}
} else if (ptype && ptype->base_type() == IVL_VT_DARRAY) {
NetEProperty*prop = new NetEProperty(net, pidx, 0);
prop->set_line(*this);
@ -3981,6 +3987,12 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
&netvector_t::atom2s32,
method_name, "$size");
}
if (method_name == "reverse") {
static const std::vector<perm_string> parm_names;
return elaborate_sys_task_method_(des, scope, prop, method_name,
"$ivl_darray_method$reverse",
parm_names);
}
}
}
}
@ -4055,11 +4067,12 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
&netvector_t::atom2s32,
method_name, "$size");
} else if (method_name == "reverse") {
cerr << get_fileline() << ": sorry: 'reverse()' "
"array sorting method is not currently supported."
<< endl;
des->errors += 1;
return 0;
static const std::vector<perm_string> parm_names;
NetESignal*sig = new NetESignal(net);
sig->set_line(*this);
return elaborate_sys_task_method_(des, scope, sig, method_name,
"$ivl_darray_method$reverse",
parm_names);
} else if (method_name=="sort") {
cerr << get_fileline() << ": sorry: 'sort()' "
"array sorting method is not currently supported."

View File

@ -6,7 +6,7 @@ on unpacked queues (int q[$]) and dynamic arrays (int da[]):
find, find_index, find_first, find_first_index, find_last,
find_last_index, min, max, unique, unique_index,
sum/product (integral)
sum/product (integral), reverse (ordering)
Behavior notes (LRM-oriented):
@ -29,6 +29,10 @@ Behavior notes (LRM-oriented):
vector types).
`product() with (expr)` reduces the expression result for each item.
* reverse() reverses the order of elements in place for queues and dynamic
arrays (integral, vector, real, string, and class-handle elements as stored
by Icarus).
* For dynamic arrays, runtime support treats storage as vvp_darray (including
atom-backed integral arrays), not only vvp_queue_vec4. See vvp/vthread.cc:
get_queue_or_darray_vec4_from_net() and the %queue/* opcodes used for
@ -39,8 +43,10 @@ Compiler / codegen touchpoints (typical):
* Elaboration: elab_expr.cc — NetESFunc $ivl_queue_method$* and with-predicate
lowering.
* Code generation: tgt-vvp/eval_object.c — eval_queue_method_find,
eval_queue_method_find_with; eval_vec4.c for non-with find*.
* VVP: vvp/vthread.cc — opcode implementations; vvp/compile.cc — opcode tables.
eval_queue_method_find_with; eval_vec4.c for non-with find*;
tgt-vvp/vvp_process.c — reverse/delete as system tasks (`%reverse/obj`, …).
* VVP: vvp/vthread.cc — opcode implementations; vvp/vvp_darray.{h,cc} —
reverse_elems(); vvp/compile.cc — opcode tables.
Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list):
@ -64,6 +70,8 @@ Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list):
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_darray_reverse.v reverse() ordering on dynamic arrays.
sv_queue_reverse.v reverse() ordering on queues.
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

@ -62,6 +62,12 @@ module test;
`check(c.d.sum() with (item + 1), 12);
`check(c.d.product() with (item + 1), 60);
c.d = '{1, 2, 3};
c.d.reverse();
`check(c.d[0], 3);
`check(c.d[1], 2);
`check(c.d[2], 1);
if (!failed)
$display("PASSED");
end

View File

@ -54,6 +54,12 @@ module test;
`check(c.q.sum() with (item + 1), 12);
`check(c.q.product() with (item + 1), 60);
c.q = '{1, 2, 3};
c.q.reverse();
`check(c.q[0], 3);
`check(c.q[1], 2);
`check(c.q[2], 1);
if (!failed)
$display("PASSED");
end

View File

@ -0,0 +1,32 @@
// Regression: dynamic array reverse() ordering method.
module top;
bit failed = 0;
`define CHK(cond) \
if (!(cond)) begin \
$display("FAILED line %0d", `__LINE__); \
failed = 1; \
end
int a[];
initial begin
a = '{1, 2, 3, 4};
a.reverse();
`CHK(a[0] === 4 && a[1] === 3 && a[2] === 2 && a[3] === 1);
a = new [1];
a[0] = 42;
a.reverse();
`CHK(a[0] === 42);
a = new [0];
a.reverse();
`CHK(a.size() === 0);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,31 @@
// Regression: queue reverse() ordering method.
module top;
bit failed = 0;
`define CHK(cond) \
if (!(cond)) begin \
$display("FAILED line %0d", `__LINE__); \
failed = 1; \
end
int q[$];
initial begin
q = '{1, 2, 3, 4};
q.reverse();
`CHK(q[0] === 4 && q[1] === 3 && q[2] === 2 && q[3] === 1);
q = '{99};
q.reverse();
`CHK(q[0] === 99);
q.delete();
q.reverse();
`CHK(q.size() === 0);
if (!failed)
$display("PASSED");
end
endmodule

View File

@ -259,6 +259,7 @@ 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_reverse vvp_tests/sv_darray_reverse.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
@ -290,6 +291,7 @@ 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_reverse vvp_tests/sv_queue_reverse.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_reverse.v",
"iverilog-args" : [ "-g2005-sv" ],
"vlog95" : {
"__comment" : "SystemVerilog dynamic array reverse()",
"type" : "CE"
}
}

View File

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

View File

@ -3454,9 +3454,10 @@ bool NetScope::check_synth(ivl_process_type_t pr_type,
bool NetSTask::check_synth(ivl_process_type_t pr_type,
const NetScope* /* scope */) const
{
if (strcmp(name(), "$ivl_darray_method$delete") == 0) {
if (strcmp(name(), "$ivl_darray_method$delete") == 0 ||
strcmp(name(), "$ivl_darray_method$reverse") == 0) {
cerr << get_fileline() << ": warning: Dynamic array "
"delete method cannot be synthesized "
"ordering/delete method cannot be synthesized "
<< get_process_type_as_string(pr_type) << endl;
} else {
cerr << get_fileline() << ": warning: System task ("

View File

@ -1691,11 +1691,41 @@ static int show_delete_method(ivl_statement_t net)
draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3);
fprintf(vvp_out, " %%delete/elem v%p_0;\n", var);
} else {
fprintf(vvp_out, " %%delete/obj v%p_0;\n", var);
fprintf(vvp_out, " %%delete/obj v%p_0;\n", var);
}
return 0;
}
static int show_reverse_method(ivl_statement_t net)
{
show_stmt_file_line(net, "reverse dynamic array or queue");
unsigned parm_count = ivl_stmt_parm_count(net);
if (parm_count != 1)
return 1;
ivl_expr_t parm = ivl_stmt_parm(net, 0);
if (ivl_expr_type(parm) == IVL_EX_PROPERTY) {
ivl_signal_t clas = ivl_expr_signal(parm);
unsigned pidx = ivl_expr_property_idx(parm);
ivl_type_t sig_type = ivl_signal_net_type(clas);
ivl_type_t arr_type = ivl_type_prop_type(sig_type, pidx);
ivl_variable_type_t bt = ivl_type_base(arr_type);
if (bt != IVL_VT_QUEUE && bt != IVL_VT_DARRAY)
return 1;
fprintf(vvp_out, " %%load/obj v%p_0;\n", clas);
fprintf(vvp_out, " %%reverse/prop/obj %u;\n", pidx);
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
return 0;
}
assert(ivl_expr_type(parm) == IVL_EX_SIGNAL);
ivl_signal_t var = ivl_expr_signal(parm);
fprintf(vvp_out, " %%reverse/obj v%p_0;\n", var);
return 0;
}
static int show_insert_method(ivl_statement_t net)
{
show_stmt_file_line(net, "queue: insert");
@ -1881,6 +1911,9 @@ static int show_system_task_call(ivl_statement_t net)
if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0)
return show_delete_method(net);
if (strcmp(stmt_name,"$ivl_darray_method$reverse") == 0)
return show_reverse_method(net);
if (strcmp(stmt_name,"$ivl_queue_method$insert") == 0)
return show_insert_method(net);

View File

@ -213,6 +213,8 @@ extern bool of_REPLICATE(vthread_t thr, vvp_code_t code);
extern bool of_RET_REAL(vthread_t thr, vvp_code_t code);
extern bool of_RET_STR(vthread_t thr, vvp_code_t code);
extern bool of_RET_VEC4(vthread_t thr, vvp_code_t code);
extern bool of_REVERSE_OBJ(vthread_t thr, vvp_code_t code);
extern bool of_REVERSE_PROP_OBJ(vthread_t thr, vvp_code_t code);
extern bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t code);
extern bool of_RETLOAD_STR(vthread_t thr, vvp_code_t code);
extern bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t code);

View File

@ -323,6 +323,8 @@ static const struct opcode_table_s opcode_table[] = {
{ "%retload/real",of_RETLOAD_REAL,1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%retload/str", of_RETLOAD_STR, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%retload/vec4",of_RETLOAD_VEC4,1,{OA_NUMBER, OA_NONE,OA_NONE} },
{ "%reverse/obj", of_REVERSE_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
{ "%reverse/prop/obj", of_REVERSE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
{ "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} },
{ "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} },

View File

@ -2610,6 +2610,62 @@ bool of_DELETE_OBJ(vthread_t thr, vvp_code_t cp)
return true;
}
/* %reverse/obj <label>
* Reverse element order of the queue or dynamic array object at this net.
*/
bool of_REVERSE_OBJ(vthread_t /*thr*/, vvp_code_t cp)
{
vvp_net_t*net = cp->net;
vvp_fun_signal_object*obj = dynamic_cast<vvp_fun_signal_object*>(net->fun);
assert(obj);
vvp_object_t stor = obj->get_object();
if (stor.test_nil())
return true;
/* Match other queue ops: peek queues before vvp_darray so concrete
queue types dispatch reverse_elems() correctly. */
if (vvp_queue* queue = stor.peek<vvp_queue>()) {
queue->reverse_elems();
return true;
}
vvp_darray*darray = stor.peek<vvp_darray>();
assert(darray);
darray->reverse_elems();
return true;
}
/* %reverse/prop/obj <property-index>
* Reverse queue/darray property storage inside the class object on stack top.
*/
bool of_REVERSE_PROP_OBJ(vthread_t thr, vvp_code_t cp)
{
size_t pid = cp->number;
vvp_object_t& top = thr->peek_object();
vvp_cobject*cobj = top.peek<vvp_cobject>();
assert(cobj);
vvp_object_t arr_obj;
cobj->get_object(pid, arr_obj, 0);
if (arr_obj.test_nil())
return true;
if (vvp_queue* queue = arr_obj.peek<vvp_queue>()) {
queue->reverse_elems();
return true;
}
vvp_darray*darray = arr_obj.peek<vvp_darray>();
assert(darray);
darray->reverse_elems();
return true;
}
/* %delete/tail <label>, idx
*
* Remove all elements after the one specified.

View File

@ -18,6 +18,7 @@
*/
# include "vvp_darray.h"
# include <algorithm>
# include <iostream>
# include <typeinfo>
@ -27,6 +28,12 @@ vvp_darray::~vvp_darray()
{
}
void vvp_darray::reverse_elems(void)
{
cerr << "XXXX reverse_elems() not implemented for "
<< typeid(*this).name() << endl;
}
void vvp_darray::set_word(unsigned, const vvp_vector4_t&)
{
cerr << "XXXX set_word(vvp_vector4_t) not implemented for " << typeid(*this).name() << endl;
@ -82,6 +89,13 @@ template <class TYPE> size_t vvp_darray_atom<TYPE>::get_size() const
return array_.size();
}
template <class TYPE> void vvp_darray_atom<TYPE>::reverse_elems(void)
{
size_t n = array_.size();
for (size_t idx = 0 ; idx < n/2 ; idx += 1)
std::swap(array_[idx], array_[n-1-idx]);
}
template <class TYPE> void vvp_darray_atom<TYPE>::set_word(unsigned adr, const vvp_vector4_t&value)
{
if (adr >= array_.size())
@ -225,6 +239,20 @@ vvp_vector4_t vvp_darray_vec4::get_bitstream(bool as_vec4)
return vec;
}
void vvp_darray_vec4::reverse_elems(void)
{
size_t n = array_.size();
for (size_t idx = 0 ; idx < n/2 ; idx += 1)
std::swap(array_[idx], array_[n-1-idx]);
}
void vvp_darray_vec2::reverse_elems(void)
{
size_t n = array_.size();
for (size_t idx = 0 ; idx < n/2 ; idx += 1)
std::swap(array_[idx], array_[n-1-idx]);
}
vvp_darray_vec2::~vvp_darray_vec2()
{
}
@ -322,6 +350,13 @@ void vvp_darray_object::shallow_copy(const vvp_object*obj)
array_[idx] = that->array_[idx];
}
void vvp_darray_object::reverse_elems(void)
{
size_t n = array_.size();
for (size_t idx = 0 ; idx < n/2 ; idx += 1)
std::swap(array_[idx], array_[n-1-idx]);
}
vvp_darray_real::~vvp_darray_real()
{
}
@ -358,6 +393,13 @@ void vvp_darray_real::shallow_copy(const vvp_object*obj)
array_[idx] = that->array_[idx];
}
void vvp_darray_real::reverse_elems(void)
{
size_t n = array_.size();
for (size_t idx = 0 ; idx < n/2 ; idx += 1)
std::swap(array_[idx], array_[n-1-idx]);
}
vvp_object* vvp_darray_real::duplicate(void) const
{
vvp_darray_real*that = new vvp_darray_real(array_.size());
@ -430,6 +472,13 @@ void vvp_darray_string::shallow_copy(const vvp_object*obj)
array_[idx] = that->array_[idx];
}
void vvp_darray_string::reverse_elems(void)
{
size_t n = array_.size();
for (size_t idx = 0 ; idx < n/2 ; idx += 1)
std::swap(array_[idx], array_[n-1-idx]);
}
vvp_object* vvp_darray_string::duplicate(void) const
{
vvp_darray_string*that = new vvp_darray_string(array_.size());
@ -769,6 +818,16 @@ void vvp_queue_string::erase_tail(unsigned idx)
queue.resize(idx);
}
void vvp_queue_real::reverse_elems(void)
{
std::reverse(queue.begin(), queue.end());
}
void vvp_queue_string::reverse_elems(void)
{
std::reverse(queue.begin(), queue.end());
}
void vvp_queue_vec4::copy_elems(vvp_object_t src, unsigned max_size)
{
if (vvp_queue*src_queue = src.peek<vvp_queue>())
@ -876,3 +935,8 @@ void vvp_queue_vec4::erase_tail(unsigned idx)
if (queue.size() > idx)
queue.resize(idx);
}
void vvp_queue_vec4::reverse_elems(void)
{
std::reverse(queue.begin(), queue.end());
}

View File

@ -46,6 +46,9 @@ class vvp_darray : public vvp_object {
virtual void get_word(unsigned adr, vvp_object_t&value);
virtual vvp_vector4_t get_bitstream(bool as_vec4);
/* In-place element order reversal (dynamic arrays and queues). */
virtual void reverse_elems(void);
};
template <class TYPE> class vvp_darray_atom : public vvp_darray {
@ -55,6 +58,7 @@ template <class TYPE> class vvp_darray_atom : public vvp_darray {
~vvp_darray_atom() override;
size_t get_size(void) const override;
void reverse_elems(void) override;
void set_word(unsigned adr, const vvp_vector4_t&value) override;
void get_word(unsigned adr, vvp_vector4_t&value) override;
void shallow_copy(const vvp_object*obj) override;
@ -78,6 +82,7 @@ class vvp_darray_vec4 : public vvp_darray {
void shallow_copy(const vvp_object*obj) override;
vvp_object* duplicate(void) const override;
vvp_vector4_t get_bitstream(bool as_vec4) override;
void reverse_elems(void) override;
private:
std::vector<vvp_vector4_t> array_;
@ -92,6 +97,7 @@ class vvp_darray_vec2 : public vvp_darray {
~vvp_darray_vec2() override;
size_t get_size(void) const override;
void reverse_elems(void) override;
void set_word(unsigned adr, const vvp_vector4_t&value) override;
void get_word(unsigned adr, vvp_vector4_t&value) override;
void shallow_copy(const vvp_object*obj) override;
@ -114,6 +120,7 @@ class vvp_darray_real : public vvp_darray {
void shallow_copy(const vvp_object*obj) override;
vvp_object* duplicate(void) const override;
vvp_vector4_t get_bitstream(bool as_vec4) override;
void reverse_elems(void) override;
private:
std::vector<double> array_;
@ -130,6 +137,7 @@ class vvp_darray_string : public vvp_darray {
void get_word(unsigned adr, std::string&value) override;
void shallow_copy(const vvp_object*obj) override;
vvp_object* duplicate(void) const override;
void reverse_elems(void) override;
private:
std::vector<std::string> array_;
@ -142,6 +150,7 @@ class vvp_darray_object : public vvp_darray {
~vvp_darray_object() override;
size_t get_size(void) const override;
void reverse_elems(void) override;
void set_word(unsigned adr, const vvp_object_t&value) override;
void get_word(unsigned adr, vvp_object_t&value) override;
void shallow_copy(const vvp_object*obj) override;
@ -196,6 +205,7 @@ class vvp_queue_real : public vvp_queue {
void pop_front(void) override { queue.pop_front(); };
void erase(unsigned idx) override;
void erase_tail(unsigned idx) override;
void reverse_elems(void) override;
private:
std::deque<double> queue;
@ -216,6 +226,7 @@ class vvp_queue_string : public vvp_queue {
void pop_front(void) override { queue.pop_front(); };
void erase(unsigned idx) override;
void erase_tail(unsigned idx) override;
void reverse_elems(void) override;
private:
std::deque<std::string> queue;
@ -236,6 +247,7 @@ class vvp_queue_vec4 : public vvp_queue {
void pop_front(void) override { queue.pop_front(); };
void erase(unsigned idx) override;
void erase_tail(unsigned idx) override;
void reverse_elems(void) override;
private:
std::deque<vvp_vector4_t> queue;