From 20bd360aec64fd998cae82733b5b62b7f999a6ea Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Wed, 15 Apr 2026 00:59:58 +0500 Subject: [PATCH] SystemVerilog: queue/darray locator methods (find*, unique*) and VVP darray support Implement array locator methods for SystemVerilog queues and dynamic arrays: find, find_index, find_first, find_first_index, find_last, find_last_index, unique, and unique_index. Support both value arguments (equality) and with (predicate) forms using implicit item and index in the iterator scope. Elaboration returns queue-typed results for first/last/index locators per LRM (empty queue when no match). Fix assignment compatibility between queues and dynamic arrays where element types match. VVP: extend %queue/size, %queue/word, %queue/find*, and %queue/unique* paths so nets holding vvp_darray (including atom-backed int[]) are handled, not only vvp_queue_vec4. Queue types still subclass vvp_darray; resolve vec4 queues before plain dynamic arrays. Fall back to the legacy get_queue_object path for non-vec4 queues. Document opcode and source file touchpoints in ivtest/ivltests/README_sv_queue_locators.txt. Add vvp regressions: sv_queue_find*, sv_queue_unique, sv_queue_find_locators_ext, sv_darray_find_locators. Made-with: Cursor --- PExpr.cc | 11 + PExpr.h | 5 + dup_expr.cc | 4 +- elab_expr.cc | 1057 +++++++++++++++-- ivtest/ivltests/README_sv_queue_locators.txt | 39 + ivtest/ivltests/sv_darray_find_locators.v | 48 + ivtest/ivltests/sv_queue_find.v | 60 + ivtest/ivltests/sv_queue_find_locators_ext.v | 59 + ivtest/ivltests/sv_queue_find_with.v | 66 + ivtest/ivltests/sv_queue_unique.v | 40 + ivtest/regress-vvp.list | 5 + ivtest/vvp_tests/sv_darray_find_locators.json | 9 + ivtest/vvp_tests/sv_queue_find.json | 9 + .../vvp_tests/sv_queue_find_locators_ext.json | 9 + ivtest/vvp_tests/sv_queue_find_with.json | 9 + ivtest/vvp_tests/sv_queue_unique.json | 9 + netdarray.cc | 17 +- netmisc.cc | 69 +- parse.y | 39 +- pform_dump.cc | 2 + tgt-vvp/eval_object.c | 432 +++++++ tgt-vvp/eval_vec4.c | 15 +- tgt-vvp/vvp_priv.h | 6 + vvp/codes.h | 24 + vvp/compile.cc | 24 + vvp/vthread.cc | 655 ++++++++++ 26 files changed, 2600 insertions(+), 122 deletions(-) create mode 100644 ivtest/ivltests/README_sv_queue_locators.txt create mode 100644 ivtest/ivltests/sv_darray_find_locators.v create mode 100644 ivtest/ivltests/sv_queue_find.v create mode 100644 ivtest/ivltests/sv_queue_find_locators_ext.v create mode 100644 ivtest/ivltests/sv_queue_find_with.v create mode 100644 ivtest/ivltests/sv_queue_unique.v create mode 100644 ivtest/vvp_tests/sv_darray_find_locators.json create mode 100644 ivtest/vvp_tests/sv_queue_find.json create mode 100644 ivtest/vvp_tests/sv_queue_find_locators_ext.json create mode 100644 ivtest/vvp_tests/sv_queue_find_with.json create mode 100644 ivtest/vvp_tests/sv_queue_unique.json diff --git a/PExpr.cc b/PExpr.cc index 6228a2eea..8aa61caf1 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -274,15 +274,24 @@ PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method, { } +void PECallFunction::set_with_clause(PExpr* with_expr) +{ + delete with_expr_; + with_expr_ = with_expr; +} + PECallFunction::~PECallFunction() { delete chain_prefix_; + delete with_expr_; } void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { if (chain_prefix_) chain_prefix_->declare_implicit_nets(scope, type); + if (with_expr_) + with_expr_->declare_implicit_nets(scope, type); for (const auto &parm : parms_) { if (parm.parm) parm.parm->declare_implicit_nets(scope, type); @@ -293,6 +302,8 @@ bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const { if (chain_prefix_ && chain_prefix_->has_aa_term(des, scope)) return true; + if (with_expr_ && with_expr_->has_aa_term(des, scope)) + return true; for (const auto &parm : parms_) { if (parm.parm && parm.parm->has_aa_term(des, scope)) return true; diff --git a/PExpr.h b/PExpr.h index 36e8884b8..99c1df05d 100644 --- a/PExpr.h +++ b/PExpr.h @@ -931,6 +931,10 @@ class PECallFunction : public PExpr { explicit PECallFunction(PExpr* chain_prefix, const pform_name_t &method, const std::list &parms); + // SystemVerilog: q.find with (expr) — iterator "item"/"index" in expr. + void set_with_clause(PExpr* with_expr); + const PExpr* peek_with_clause(void) const { return with_expr_; } + ~PECallFunction() override; // For chained-call resolution (path is only the final method name). @@ -957,6 +961,7 @@ class PECallFunction : public PExpr { std::vector parms_; // If non-null, this call is prefix().tail_name(...) (SV method chain). PExpr* chain_prefix_ = nullptr; + PExpr* with_expr_ = nullptr; // For system functions. bool is_overridden_; diff --git a/dup_expr.cc b/dup_expr.cc index 4e4f2225a..5f9ae0ba3 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -235,7 +235,9 @@ NetESelect* NetESelect::dup_expr() const NetESFunc* NetESFunc::dup_expr() const { - NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms(), is_overridden_); + NetESFunc*tmp = net_type() + ? new NetESFunc(name_, net_type(), nparms()) + : new NetESFunc(name_, type_, expr_width(), nparms(), is_overridden_); ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); diff --git a/elab_expr.cc b/elab_expr.cc index 35b1d18b7..bb6d34eb1 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -41,9 +41,100 @@ # include "util.h" # include "ivl_assert.h" # include "map_named_args.h" +# include "HName.h" +# include "netvector.h" using namespace std; +static netqueue_t ivl_queue_unique_index_ret(&netvector_t::atom2s32, -1); + +static bool queue_method_element_is_integral_vec4(ivl_type_t et) +{ + ivl_variable_type_t bt = ivl_type_base(et); + return bt == IVL_VT_BOOL || bt == IVL_VT_LOGIC; +} + +/* One argument: value to match using == (same as find with (item == arg)). */ +static NetExpr* elab_queue_locator_cmp_arg(Design* des, NetScope* scope, + const LineInfo& loc, + const vector& parms, + ivl_type_t element_type) +{ + if (parms.size() != 1) { + cerr << loc.get_fileline() << ": error: Array locator method requires " + "exactly one argument (value to compare using ==)." << endl; + des->errors += 1; + return 0; + } + const named_pexpr_t& arg = parms.front(); + if (arg.parm == 0) { + cerr << loc.get_fileline() << ": error: Missing argument to array locator method." + << endl; + des->errors += 1; + return 0; + } + unsigned ew = ivl_type_packed_width(element_type); + return elab_and_eval(des, scope, arg.parm, (int)ew, false, false, + ivl_type_base(element_type)); +} + +/* Elaborate `with (expr)` for array locator methods: implicit item/index nets. */ +static NetExpr* elab_queue_locator_with_predicate( + Design* des, NetScope* scope, const LineInfo& loc, + PExpr* with_expr, NetExpr* queue_arg, ivl_type_t element_type, + ivl_type_t result_type, perm_string method_suffix) +{ + static unsigned with_scope_counter = 0; + char buf[64]; + snprintf(buf, sizeof buf, "$ivl_qwith%u", with_scope_counter++); + NetScope* ws = new NetScope(scope, hname_t(lex_strings.make(buf)), + NetScope::BEGIN_END); + ws->set_line(&loc); + + unsigned ew = ivl_type_packed_width(element_type); + const netvector_t* item_vec = new netvector_t( + ivl_type_base(element_type), (long)ew - 1, 0, + ivl_type_signed(element_type)); + NetNet* item_net = new NetNet(ws, lex_strings.make("item"), NetNet::REG, + item_vec); + item_net->set_line(loc); + item_net->local_flag(true); + + NetNet* index_net = new NetNet(ws, lex_strings.make("index"), NetNet::REG, + &netvector_t::atom2s32); + index_net->set_line(loc); + index_net->local_flag(true); + + NetExpr* pred = elab_and_eval(des, ws, with_expr, 1, false, false, + IVL_VT_BOOL); + if (!pred) + return 0; + + perm_string sfunc_name; + if (method_suffix == "find") + sfunc_name = lex_strings.make("$ivl_queue_method$find_with"); + else if (method_suffix == "find_index") + sfunc_name = lex_strings.make("$ivl_queue_method$find_index_with"); + else if (method_suffix == "find_first") + sfunc_name = lex_strings.make("$ivl_queue_method$find_first_with"); + else if (method_suffix == "find_first_index") + sfunc_name = lex_strings.make("$ivl_queue_method$find_first_index_with"); + else if (method_suffix == "find_last") + sfunc_name = lex_strings.make("$ivl_queue_method$find_last_with"); + else if (method_suffix == "find_last_index") + sfunc_name = lex_strings.make("$ivl_queue_method$find_last_index_with"); + else + ivl_assert(loc, 0); + + NetESFunc* out = new NetESFunc(sfunc_name, result_type, 4); + out->set_line(loc); + out->parm(0, queue_arg); + out->parm(1, pred); + out->parm(2, new NetESignal(item_net)); + out->parm(3, new NetESignal(index_net)); + return out; +} + bool type_is_vectorable(ivl_variable_type_t type) { switch (type) { @@ -1645,6 +1736,30 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } + if (method_name == "find" || method_name == "find_index") { + expr_type_ = IVL_VT_QUEUE; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + return expr_width_; + } + + if (method_name == "find_first" || method_name == "find_last") { + expr_type_ = darray->element_base_type(); + expr_width_ = darray->element_width(); + min_width_ = expr_width_; + signed_flag_ = darray->get_signed(); + return expr_width_; + } + + if (method_name == "find_first_index" || method_name == "find_last_index") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = 32; + min_width_ = 32; + signed_flag_= true; + return expr_width_; + } + return 0; } @@ -1676,6 +1791,38 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } + if (method_name == "unique" || method_name == "unique_index") { + expr_type_ = IVL_VT_QUEUE; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + return expr_width_; + } + + if (method_name == "find" || method_name == "find_index") { + expr_type_ = IVL_VT_QUEUE; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + return expr_width_; + } + + if (method_name == "find_first" || method_name == "find_last") { + expr_type_ = darray->element_base_type(); + expr_width_ = darray->element_width(); + min_width_ = expr_width_; + signed_flag_ = darray->get_signed(); + return expr_width_; + } + + if (method_name == "find_first_index" || method_name == "find_last_index") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = 32; + min_width_ = 32; + signed_flag_= true; + return expr_width_; + } + return 0; } @@ -3603,6 +3750,247 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, sys_expr->parm(0, prop); return sys_expr; } + if (method_name == "unique") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: unique() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$unique", + static_cast(queue), 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "unique_index") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: unique_index() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$unique_index", + static_cast(&ivl_queue_unique_index_ret), 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "find") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, static_cast(queue), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find", + static_cast(queue), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_first") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_first() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(queue), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_first", + static_cast(queue), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_first_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_first_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast( + &ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_first_index", + static_cast( + &ivl_queue_unique_index_ret), + 2); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_last") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_last() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(queue), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_last", + static_cast(queue), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_last_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_last_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast( + &ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_last_index", + static_cast( + &ivl_queue_unique_index_ret), + 2); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + sys_expr->parm(1, cmp); + return sys_expr; + } } if (ptype && ptype->base_type() == IVL_VT_DARRAY) { NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); @@ -3714,6 +4102,194 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, return sys_expr; } + const netdarray_t*dar = search_results.net->darray_type(); + ivl_assert(*this, dar); + ivl_type_t element_type = dar->element_type(); + const netqueue_t*queue = search_results.net->queue_type(); + ivl_type_t queue_rtype = queue ? static_cast(queue) + : static_cast(dar); + + if (method_name == "find") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array find() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + queue_rtype, method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find", queue_rtype, 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array find_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_first") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array find_first() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + queue_rtype, method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_first", queue_rtype, 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_first_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array find_first_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_first_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_last") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array find_last() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + queue_rtype, method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_last", queue_rtype, 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_last_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array find_last_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_last_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + cerr << get_fileline() << ": error: Method " << method_name << " is not a dynamic array method." << endl; return 0; @@ -3766,6 +4342,230 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, return sys_expr; } + if (method_name == "unique") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: unique() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$unique", + static_cast(queue), 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + + if (method_name == "unique_index") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: unique_index() method " + << "takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$unique_index", + static_cast(&ivl_queue_unique_index_ret), 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + + if (method_name == "find") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(queue), method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find", + static_cast(queue), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_first") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_first() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(queue), method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_first", + static_cast(queue), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_first_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_first_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_first_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_last") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_last() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(queue), method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_last", + static_cast(queue), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + if (method_name == "find_last_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue find_last_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + if (with_expr_) { + 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, + static_cast(&ivl_queue_unique_index_ret), + method_name); + } + NetExpr* cmp = elab_queue_locator_cmp_arg( + des, scope, *this, parms_, element_type); + if (!cmp) + return 0; + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$find_last_index", + static_cast(&ivl_queue_unique_index_ret), 2); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + sys_expr->parm(1, cmp); + return sys_expr; + } + cerr << get_fileline() << ": error: Method " << method_name << " is not a queue method." << endl; des->errors += 1; @@ -4929,9 +5729,85 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << "Found net " << net->name() << " for expr " << *this << endl; } + // Queue methods (e.g. q.unique) need NetESFunc. The width-based + // elaborate_expr_() path can still fall through to NetESignal(net) + // for typed queue-to-queue assignments, so resolve methods here. + // Use path_.back(): symbol_search may leave path_tail empty while + // the method name is still the last component of path_. + if (sr.net->queue_type() && path_.size() > 1) { + const name_component_t member_comp = path_.back(); + const netqueue_t*queue = sr.net->queue_type(); + ivl_type_t element_type = queue->element_type(); + if (member_comp.name == "size") { + NetESFunc*fun = new NetESFunc("$size", + &netvector_t::atom2s32, + 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + if (member_comp.name == "pop_back") { + NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_back", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + if (member_comp.name == "pop_front") { + NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_front", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + if (member_comp.name == "unique") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$unique", + static_cast(queue), 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + if (member_comp.name == "unique_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$unique_index", + static_cast(&ivl_queue_unique_index_ret), 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + cerr << get_fileline() << ": error: Unknown or unsupported queue " + << "member `" << member_comp.name << "'." << endl; + des->errors += 1; + return 0; + } + ivl_type_t check_type = ntype; if (const netdarray_t*array_type = dynamic_cast (ntype)) { - if (array_type->type_compatible(net->net_type())) { + if (array_type->type_compatible(net->net_type()) + && sr.path_tail.empty()) { NetESignal*tmp = new NetESignal(net); tmp->set_line(*this); return tmp; @@ -5148,9 +6024,99 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, sr.path_tail); } - // If this is an array object, and there are members in - // the sr.path_tail, check for array properties. - if (sr.net->darray_type() && !sr.path_tail.empty()) { + // If this is a queue object, handle queue methods before the + // dynamic-array branch: netqueue_t inherits netdarray_t, so + // darray_type() is also true for queues. + // For q.unique, path_ is {q,unique} but symbol_search may leave + // path_tail empty; the method is then path_.back(). + if (sr.net->queue_type() + && (path_.size() > 1 || !sr.path_tail.empty())) { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Ident " << sr.path_head + << " looking for queue property " << sr.path_tail + << endl; + } + + const name_component_t member_comp = path_.size() > 1 + ? path_.back() + : sr.path_tail.front(); + const netqueue_t*queue = sr.net->queue_type(); + ivl_type_t element_type = queue->element_type(); + if (member_comp.name == "size") { + NetESFunc*fun = new NetESFunc("$size", + &netvector_t::atom2s32, + 1); + fun->set_line(*this); + + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + + fun->parm(0, arg); + return fun; + } + if (member_comp.name == "pop_back") { + NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_back", + element_type, 1); + fun->set_line(*this); + + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + + fun->parm(0, arg); + return fun; + } + + if (member_comp.name == "pop_front") { + NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_front", + element_type, 1); + fun->set_line(*this); + + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + + fun->parm(0, arg); + return fun; + } + + if (member_comp.name == "unique") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$unique", + static_cast(queue), 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + + if (member_comp.name == "unique_index") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue unique_index() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$unique_index", + static_cast(&ivl_queue_unique_index_ret), 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; + } + } + + // Dynamic array (not queue) — array location / reduction methods. + if (sr.net->darray_type() && !sr.net->queue_type() + && !sr.path_tail.empty()) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr: " "Ident " << sr.path_head @@ -5171,40 +6137,16 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, fun->parm(0, arg); return fun; - } else if (member_comp.name == "find") { - cerr << get_fileline() << ": sorry: 'find()' " - "array location method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; - } else if (member_comp.name == "find_index") { - cerr << get_fileline() << ": sorry: 'find_index()' " - "array location method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; - } else if (member_comp.name == "find_first") { - cerr << get_fileline() << ": sorry: 'find_first()' " - "array location method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; - } else if (member_comp.name == "find_first_index") { - cerr << get_fileline() << ": sorry: 'find_first_index()' " - "array location method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; - } else if (member_comp.name == "find_last") { - cerr << get_fileline() << ": sorry: 'find_last()' " - "array location method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; - } else if (member_comp.name == "find_last_index") { - cerr << get_fileline() << ": sorry: 'find_last_index()' " - "array location method is not currently " - "implemented." << endl; + } else if (member_comp.name == "find" || + member_comp.name == "find_index" || + member_comp.name == "find_first" || + member_comp.name == "find_first_index" || + member_comp.name == "find_last" || + member_comp.name == "find_last_index") { + cerr << get_fileline() << ": error: Array locator method `" + << member_comp.name << "' must be called with one " + << "argument, e.g. " << member_comp.name << "(value)." + << endl; des->errors += 1; return 0; } else if (member_comp.name == "min") { @@ -5266,45 +6208,6 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, } } - // If this is a queue object, and there are members in - // the sr.path_tail, check for array properties. - if (sr.net->queue_type() && !sr.path_tail.empty()) { - if (debug_elaborate) { - cerr << get_fileline() << ": PEIdent::elaborate_expr: " - << "Ident " << sr.path_head - << " looking for queue property " << sr.path_tail - << endl; - } - - ivl_assert(*this, sr.path_tail.size() == 1); - const name_component_t member_comp = sr.path_tail.front(); - const netqueue_t*queue = sr.net->queue_type(); - ivl_type_t element_type = queue->element_type(); - if (member_comp.name == "pop_back") { - NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_back", - element_type, 1); - fun->set_line(*this); - - NetESignal*arg = new NetESignal(sr.net); - arg->set_line(*sr.net); - - fun->parm(0, arg); - return fun; - } - - if (member_comp.name == "pop_front") { - NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_front", - element_type, 1); - fun->set_line(*this); - - NetESignal*arg = new NetESignal(sr.net); - arg->set_line(*sr.net); - - fun->parm(0, arg); - return fun; - } - } - if ((sr.net->data_type() == IVL_VT_STRING) && !sr.path_tail.empty()) { if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_expr: " diff --git a/ivtest/ivltests/README_sv_queue_locators.txt b/ivtest/ivltests/README_sv_queue_locators.txt new file mode 100644 index 000000000..b79af2018 --- /dev/null +++ b/ivtest/ivltests/README_sv_queue_locators.txt @@ -0,0 +1,39 @@ +SystemVerilog array locator methods (queues and dynamic arrays) +================================================================ + +This directory includes regression tests for IEEE 1800 locator methods +on unpacked queues (int q[$]) and dynamic arrays (int da[]): + + find, find_index, find_first, find_first_index, find_last, + find_last_index, unique, unique_index + +Behavior notes (LRM-oriented): + + * Methods may use a value argument, e.g. q.find(2), or a predicate with + `with`, e.g. q.find() with (item == 2). A `with` clause must not be + combined with a method value argument. + + * find_first, find_last, find_first_index, and find_last_index return a + queue with zero or one element; no match yields an empty queue (not a + scalar sentinel). + + * 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 + size, word read, find, and unique. + +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. + +Regression tests (see ivtest/vvp_tests/*.json and regress-vvp.list): + + sv_queue_find.v Value-argument find* (equality), empty cases. + sv_queue_find_with.v find* with `with` (item, index). + sv_queue_find_locators_ext.v Longer queue, compound predicates. + sv_queue_unique.v unique / unique_index. + sv_darray_find_locators.v Same locator patterns on int[] dynamic array. diff --git a/ivtest/ivltests/sv_darray_find_locators.v b/ivtest/ivltests/sv_darray_find_locators.v new file mode 100644 index 000000000..009a7d994 --- /dev/null +++ b/ivtest/ivltests/sv_darray_find_locators.v @@ -0,0 +1,48 @@ +// Regression: dynamic array locator methods (find* with predicate); results are queues. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int array[] = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + int res[$]; + + initial begin + res = array.find() with (item > 3); + `CHK(res.size == 5); + `CHK(res[0] == 4); + `CHK(res[1] == 7); + `CHK(res[2] == 5); + `CHK(res[3] == 7); + `CHK(res[4] == 6); + + res = array.find_index() with (item == 4); + `CHK(res.size == 1); + `CHK(res[0] == 0); + + res = array.find_first() with (item < 5 && item >= 3); + `CHK(res.size == 1); + `CHK(res[0] == 4); + + res = array.find_first_index() with (item > 5); + `CHK(res.size == 1); + `CHK(res[0] == 1); + + res = array.find_last() with (item <= 7 && item > 3); + `CHK(res.size == 1); + `CHK(res[0] == 6); + + res = array.find_last_index() with (item < 3); + `CHK(res.size == 1); + `CHK(res[0] == 8); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_find.v b/ivtest/ivltests/sv_queue_find.v new file mode 100644 index 000000000..76e3b78dd --- /dev/null +++ b/ivtest/ivltests/sv_queue_find.v @@ -0,0 +1,60 @@ +// Regression: queue find* locator methods (one argument: value matched with ==). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int f[$]; + int ix[$]; + int ff[$]; + int fi[$]; + int lf[$]; + int li[$]; + + initial begin + q.delete(); + q.push_back(2); + q.push_back(1); + q.push_back(2); + q.push_back(3); + + f = q.find(2); + `CHK(f.size == 2); + `CHK(f[0] == 2); + `CHK(f[1] == 2); + + ix = q.find_index(2); + `CHK(ix.size == 2); + `CHK(ix[0] == 0); + `CHK(ix[1] == 2); + + ff = q.find_first(3); + `CHK(ff.size == 1); + `CHK(ff[0] == 3); + fi = q.find_first_index(3); + `CHK(fi.size == 1); + `CHK(fi[0] == 3); + + lf = q.find_last(2); + `CHK(lf.size == 1); + `CHK(lf[0] == 2); + li = q.find_last_index(2); + `CHK(li.size == 1); + `CHK(li[0] == 2); + + ff = q.find_first(99); + `CHK(ff.size == 0); + fi = q.find_first_index(99); + `CHK(fi.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_find_locators_ext.v b/ivtest/ivltests/sv_queue_find_locators_ext.v new file mode 100644 index 000000000..e6b279c5a --- /dev/null +++ b/ivtest/ivltests/sv_queue_find_locators_ext.v @@ -0,0 +1,59 @@ +// Regression: queue locator methods with `with` — compound predicates and longer sequences. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int res[$]; + + initial begin + q.delete(); + q.push_back(4); + q.push_back(7); + q.push_back(2); + q.push_back(5); + q.push_back(7); + q.push_back(1); + q.push_back(6); + q.push_back(3); + q.push_back(1); + + res = q.find() with (item > 3); + `CHK(res.size == 5); + `CHK(res[0] == 4); + `CHK(res[1] == 7); + `CHK(res[2] == 5); + `CHK(res[3] == 7); + `CHK(res[4] == 6); + + res = q.find_index() with (item == 4); + `CHK(res.size == 1); + `CHK(res[0] == 0); + + res = q.find_first() with (item < 5 && item >= 3); + `CHK(res.size == 1); + `CHK(res[0] == 4); + + res = q.find_first_index() with (item > 5); + `CHK(res.size == 1); + `CHK(res[0] == 1); + + res = q.find_last() with (item <= 7 && item > 3); + `CHK(res.size == 1); + `CHK(res[0] == 6); + + res = q.find_last_index() with (item < 3); + `CHK(res.size == 1); + `CHK(res[0] == 8); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_find_with.v b/ivtest/ivltests/sv_queue_find_with.v new file mode 100644 index 000000000..59763ce16 --- /dev/null +++ b/ivtest/ivltests/sv_queue_find_with.v @@ -0,0 +1,66 @@ +// Regression: queue find* locator methods with `with (predicate)` (item, index). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int f[$]; + int ix[$]; + int ff[$]; + int fi[$]; + int lf[$]; + int li[$]; + + initial begin + q.delete(); + q.push_back(2); + q.push_back(1); + q.push_back(2); + q.push_back(3); + + f = q.find() with (item == 2); + `CHK(f.size == 2); + `CHK(f[0] == 2); + `CHK(f[1] == 2); + + ix = q.find_index() with (item == 2); + `CHK(ix.size == 2); + `CHK(ix[0] == 0); + `CHK(ix[1] == 2); + + ix = q.find_index() with (index > 0); + `CHK(ix.size == 3); + `CHK(ix[0] == 1); + `CHK(ix[1] == 2); + `CHK(ix[2] == 3); + + ff = q.find_first() with (item == 3); + `CHK(ff.size == 1); + `CHK(ff[0] == 3); + fi = q.find_first_index() with (item == 2 && index > 0); + `CHK(fi.size == 1); + `CHK(fi[0] == 2); + + lf = q.find_last() with (item < 3); + `CHK(lf.size == 1); + `CHK(lf[0] == 2); + li = q.find_last_index() with (item == 2); + `CHK(li.size == 1); + `CHK(li[0] == 2); + + ff = q.find_first() with (item == 99); + `CHK(ff.size == 0); + fi = q.find_first_index() with (item == 99); + `CHK(fi.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_unique.v b/ivtest/ivltests/sv_queue_unique.v new file mode 100644 index 000000000..942c20532 --- /dev/null +++ b/ivtest/ivltests/sv_queue_unique.v @@ -0,0 +1,40 @@ +// Regression: queue unique() and unique_index() for integral packed queues. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int u[$]; + int ix[$]; + + initial begin + q.delete(); + q.push_back(1); + q.push_back(2); + q.push_back(1); + q.push_back(3); + q.push_back(2); + + u = q.unique(); + `CHK(u.size == 3); + `CHK(u[0] == 1); + `CHK(u[1] == 2); + `CHK(u[2] == 3); + + ix = q.unique_index(); + `CHK(ix.size == 3); + `CHK(ix[0] == 0); + `CHK(ix[1] == 1); + `CHK(ix[2] == 3); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index df3fd4190..8e1fc2ae2 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -252,6 +252,7 @@ sv_const_fail7 vvp_tests/sv_const_fail7.json sv_const_fail8 vvp_tests/sv_const_fail8.json sv_const_fail9 vvp_tests/sv_const_fail9.json sv_darray_assign_op vvp_tests/sv_darray_assign_op.json +sv_darray_find_locators vvp_tests/sv_darray_find_locators.json sv_default_port_value1 vvp_tests/sv_default_port_value1.json sv_default_port_value2 vvp_tests/sv_default_port_value2.json sv_default_port_value3 vvp_tests/sv_default_port_value3.json @@ -271,6 +272,10 @@ sv_module_port3 vvp_tests/sv_module_port3.json sv_module_port4 vvp_tests/sv_module_port4.json sv_parameter_type vvp_tests/sv_parameter_type.json sv_queue_assign_op vvp_tests/sv_queue_assign_op.json +sv_queue_unique vvp_tests/sv_queue_unique.json +sv_queue_find vvp_tests/sv_queue_find.json +sv_queue_find_locators_ext vvp_tests/sv_queue_find_locators_ext.json +sv_queue_find_with vvp_tests/sv_queue_find_with.json sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json sdf_header vvp_tests/sdf_header.json task_return1 vvp_tests/task_return1.json diff --git a/ivtest/vvp_tests/sv_darray_find_locators.json b/ivtest/vvp_tests/sv_darray_find_locators.json new file mode 100644 index 000000000..e953c4196 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_find_locators.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_find_locators.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array locator methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_find.json b/ivtest/vvp_tests/sv_queue_find.json new file mode 100644 index 000000000..ea23c13ad --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_find.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_find.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queues", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_find_locators_ext.json b/ivtest/vvp_tests/sv_queue_find_locators_ext.json new file mode 100644 index 000000000..a3340f8a6 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_find_locators_ext.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_find_locators_ext.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue locator methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_find_with.json b/ivtest/vvp_tests/sv_queue_find_with.json new file mode 100644 index 000000000..65d0ed2f0 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_find_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_find_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queues", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_unique.json b/ivtest/vvp_tests/sv_queue_unique.json new file mode 100644 index 000000000..173c1e4e7 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_unique.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_unique.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queues", + "type" : "CE" + } +} diff --git a/netdarray.cc b/netdarray.cc index 0e45cabe8..7b9608c94 100644 --- a/netdarray.cc +++ b/netdarray.cc @@ -39,11 +39,22 @@ ivl_variable_type_t netdarray_t::base_type(void) const bool netdarray_t::test_equivalence(ivl_type_t that) const { - // Queues and dynamic arrays are not equivalent, so check for the base - // type to make sure both are either dynamic array or queue. - if (base_type() != that->base_type()) + const netdarray_t* that_da = dynamic_cast(that); + if (!that_da) return false; + /* Queue vs unpacked dynamic array: same element type is assignable + * (e.g. int[$] = array.find_first(...), locator return is a queue). */ + if (base_type() != that->base_type()) { + if ((base_type() == IVL_VT_QUEUE && + that->base_type() == IVL_VT_DARRAY) || + (base_type() == IVL_VT_DARRAY && + that->base_type() == IVL_VT_QUEUE)) { + return element_type()->type_equivalent(that_da->element_type()); + } + return false; + } + return test_compatibility(that); } diff --git a/netmisc.cc b/netmisc.cc index 9e4162800..d2ca275fa 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -890,32 +890,38 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, if (tmp == 0) return 0; if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) { - switch (tmp->expr_type()) { - case IVL_VT_BOOL: - case IVL_VT_LOGIC: - case IVL_VT_REAL: - break; - default: - cerr << tmp->get_fileline() << ": error: " - "The expression '" << *pe << "' cannot be implicitly " - "cast to the target type." << endl; - des->errors += 1; - delete tmp; - return 0; - } - switch (cast_type) { - case IVL_VT_REAL: - tmp = cast_to_real(tmp); - break; - case IVL_VT_BOOL: - tmp = cast_to_int2(tmp, pos_context_width); - break; - case IVL_VT_LOGIC: - tmp = cast_to_int4(tmp, pos_context_width); - break; - default: - break; - } + bool qdar_mix = + (cast_type == IVL_VT_QUEUE || cast_type == IVL_VT_DARRAY) && + (tmp->expr_type() == IVL_VT_QUEUE || + tmp->expr_type() == IVL_VT_DARRAY); + if (!qdar_mix) { + switch (tmp->expr_type()) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + case IVL_VT_REAL: + break; + default: + cerr << tmp->get_fileline() << ": error: " + "The expression '" << *pe << "' cannot be implicitly " + "cast to the target type." << endl; + des->errors += 1; + delete tmp; + return 0; + } + switch (cast_type) { + case IVL_VT_REAL: + tmp = cast_to_real(tmp); + break; + case IVL_VT_BOOL: + tmp = cast_to_int2(tmp, pos_context_width); + break; + case IVL_VT_LOGIC: + tmp = cast_to_int4(tmp, pos_context_width); + break; + default: + break; + } + } } eval_expr(tmp, context_width); @@ -958,12 +964,23 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, compatible = lv_net_type->type_compatible(tmp->net_type()); else compatible = false; + } else if ((cast_type == IVL_VT_QUEUE || cast_type == IVL_VT_DARRAY) && + (expr_type == IVL_VT_QUEUE || expr_type == IVL_VT_DARRAY)) { + if (tmp->net_type()) + compatible = lv_net_type->type_compatible(tmp->net_type()); + else + compatible = cast_type == expr_type; } else if (cast_type == IVL_VT_NO_TYPE) { compatible = true; } else { compatible = cast_type == expr_type; } + /* R-value may report a coarse expr_type while net_type matches (e.g. + * queue locators); prefer structural compatibility when available. */ + if (!compatible && tmp->net_type()) + compatible = lv_net_type->type_compatible(tmp->net_type()); + if (!compatible) { // Catch some special cases. switch (cast_type) { diff --git a/parse.y b/parse.y index 12e7e43b1..4a9274666 100644 --- a/parse.y +++ b/parse.y @@ -3887,6 +3887,21 @@ call_chain_expr delete $5; $$ = tmp; } + | call_chain_expr K_with '(' expression ')' + { PECallFunction* cf = dynamic_cast($1); + if (cf == 0) { + if (gn_system_verilog()) + yyerror(@2, "Error: `with` clause must follow a method call."); + else + yyerror(@2, "Error: Enable SystemVerilog for `with` clause on method calls."); + delete $1; + delete $4; + $$ = 0; + } else { + cf->set_with_clause($4); + $$ = cf; + } + } ; expr_primary @@ -3953,6 +3968,15 @@ expr_primary | call_chain_expr { $$ = $1; } + /* SV: q.find with (item == 1) — iterator names item, index */ + | hierarchy_identifier K_with '(' expression ')' + { std::vector empty; + PECallFunction* tmp = new PECallFunction(*$1, empty); + FILE_NAME(tmp, @1); + tmp->set_with_clause($4); + delete $1; + $$ = tmp; + } | hierarchy_identifier { PEIdent*tmp = pform_new_ident(@1, *$1); FILE_NAME(tmp, @1); @@ -3976,14 +4000,6 @@ expr_primary $$ = tmp; delete nm; } - | hierarchy_identifier '.' K_unique - { pform_name_t * nm = $1; - nm->push_back(name_component_t(lex_strings.make("unique"))); - PEIdent*tmp = pform_new_ident(@1, *nm); - FILE_NAME(tmp, @1); - $$ = tmp; - delete nm; - } | hierarchy_identifier '.' K_xor { pform_name_t * nm = $1; nm->push_back(name_component_t(lex_strings.make("xor"))); @@ -3992,7 +4008,6 @@ expr_primary $$ = tmp; delete nm; } - | package_scope hierarchy_identifier { lex_in_package_scope(0); $$ = pform_package_ident(@2, $1, $2); @@ -4492,6 +4507,12 @@ hierarchy_identifier delete[]$3; $$ = tmp; } + /* "unique" is a keyword (K_unique) but also a queue/array method name. */ + | hierarchy_identifier '.' K_unique + { pform_name_t * tmp = $1; + tmp->push_back(name_component_t(lex_strings.make("unique"))); + $$ = tmp; + } | hierarchy_identifier '[' expression ']' { pform_name_t * tmp = $1; name_component_t&tail = tmp->back(); diff --git a/pform_dump.cc b/pform_dump.cc index 688ecf54c..313d94cf5 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -426,6 +426,8 @@ void PECallFunction::dump(ostream &out) const out << "."; } out << path_ << "(" << parms_ << ")"; + if (peek_with_clause()) + out << " with (" << *peek_with_clause() << ")"; } void PECastSize::dump(ostream &out) const diff --git a/tgt-vvp/eval_object.c b/tgt-vvp/eval_object.c index 055ef38ae..dfcc1ac1d 100644 --- a/tgt-vvp/eval_object.c +++ b/tgt-vvp/eval_object.c @@ -257,6 +257,418 @@ static int eval_object_ufunc(ivl_expr_t ex) return 0; } +static unsigned queue_unique_src_elem_wid(ivl_expr_t arg) +{ + ivl_type_t src_q = 0; + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + src_q = ivl_type_prop_type(ivl_signal_net_type(cl), pidx); + } else if (ivl_expr_type(arg) == IVL_EX_SIGNAL) { + src_q = ivl_signal_net_type(ivl_expr_signal(arg)); + } else { + return 0; + } + if (ivl_type_base(src_q) == IVL_VT_QUEUE || + ivl_type_base(src_q) == IVL_VT_DARRAY) + return ivl_type_packed_width(ivl_type_element(src_q)); + return 0; +} + +static int eval_queue_method_unique(ivl_expr_t expr) +{ + const char*name = ivl_expr_name(expr); + ivl_expr_t arg = ivl_expr_parm(expr, 0); + unsigned elem_wid = queue_unique_src_elem_wid(arg); + if (elem_wid == 0) + return 1; + + if (strcmp(name, "$ivl_queue_method$unique") == 0) { + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/unique/prop/v %u, %u;\n", pidx, + elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(arg); + fprintf(vvp_out, " %%queue/unique/v v%p_0, %u;\n", sig, + elem_wid); + } + return 0; + } + + if (strcmp(name, "$ivl_queue_method$unique_index") == 0) { + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/unique/index/prop/v %u, %u;\n", + pidx, elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(arg); + fprintf(vvp_out, " %%queue/unique/index/v v%p_0, %u;\n", + sig, elem_wid); + } + return 0; + } + + return 1; +} + +/* + * Array locator methods with `with (predicate)` — parms: + * 0: queue, 1: predicate, 2: item ivl_signal, 3: index ivl_signal + */ +static int eval_queue_method_find_with(ivl_expr_t expr) +{ + const char* name = ivl_expr_name(expr); + if (ivl_expr_parms(expr) != 4) + return 1; + if (strncmp(name, "$ivl_queue_method$", sizeof("$ivl_queue_method$") - 1) != 0) + return 1; + if (strstr(name, "_with") == 0) + return 1; + + ivl_expr_t qarg = ivl_expr_parm(expr, 0); + ivl_expr_t pred = ivl_expr_parm(expr, 1); + ivl_signal_t item_sig = ivl_expr_signal(ivl_expr_parm(expr, 2)); + ivl_signal_t idx_sig = ivl_expr_signal(ivl_expr_parm(expr, 3)); + unsigned elem_wid = queue_unique_src_elem_wid(qarg); + if (elem_wid == 0) + return 1; + + int mode = -1; + if (strcmp(name, "$ivl_queue_method$find_with") == 0) + mode = 0; + else if (strcmp(name, "$ivl_queue_method$find_index_with") == 0) + mode = 1; + else if (strcmp(name, "$ivl_queue_method$find_first_with") == 0) + mode = 2; + else if (strcmp(name, "$ivl_queue_method$find_first_index_with") == 0) + mode = 3; + else if (strcmp(name, "$ivl_queue_method$find_last_with") == 0) + mode = 4; + else if (strcmp(name, "$ivl_queue_method$find_last_index_with") == 0) + mode = 5; + else + return 1; + + int i_reg = allocate_word(); + int n_reg = allocate_word(); + unsigned lab_top = local_count++; + unsigned lab_loop_end = local_count++; + unsigned lab_nom = local_count++; + unsigned lab_found = local_count++; + unsigned lab_end = local_count++; + int reverse = (mode == 4 || mode == 5); + unsigned append_wid = (mode == 1) ? 32 : elem_wid; + + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%prop/queue/size %u;\n", pidx); + fprintf(vvp_out, " %%ix/vec4/s %u;\n", n_reg); + if (mode == 0 || mode == 1) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + } + if (!reverse) { + fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", i_reg); + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + } else { + fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", i_reg); + fprintf(vvp_out, " %%ix/mov %u, %u;\n", i_reg, n_reg); + fprintf(vvp_out, " %%ix/sub %u, 1, 0;\n", i_reg); + } + fprintf(vvp_out, "T_%u.%u ; queue with loop\n", thread_count, lab_top); + if (!reverse) { + fprintf(vvp_out, " %%cmpix/ltu %u, %u;\n", i_reg, n_reg); + fprintf(vvp_out, " %%jmp/0 T_%u.%u, 4;\n", thread_count, + lab_loop_end); + } else { + fprintf(vvp_out, " %%cmpix/slt0 %u;\n", i_reg); + fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, + lab_loop_end); + } + /* cmpix leaves flag 4 set; %queue/word* uses flag 4 for X push */ + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + + fprintf(vvp_out, " %%queue/word/prop/v %u, %u, %u;\n", pidx, + elem_wid, i_reg); + fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", item_sig, + elem_wid); + 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); + + int pf = draw_eval_condition(pred); + fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d;\n", thread_count, lab_nom, + pf); + clr_flag(pf); + + if (mode == 0) { + fprintf(vvp_out, " %%load/vec4 v%p_0;\n", item_sig); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", append_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else if (mode == 1) { + 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 == 2) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + fprintf(vvp_out, " %%load/vec4 v%p_0;\n", item_sig); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_found); + } else if (mode == 3) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + 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_found); + } else if (mode == 4) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + fprintf(vvp_out, " %%load/vec4 v%p_0;\n", item_sig); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_found); + } else { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + 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_found); + } + + fprintf(vvp_out, "T_%u.%u ; nomatch\n", thread_count, lab_nom); + if (!reverse) { + fprintf(vvp_out, " %%ix/add %u, 1, 0;\n", i_reg); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); + } else { + fprintf(vvp_out, " %%ix/sub %u, 1, 0;\n", i_reg); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); + } + + fprintf(vvp_out, "T_%u.%u ; found (queue prop)\n", thread_count, + lab_found); + fprintf(vvp_out, " %%pop/obj 1, 1;\n"); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_end); + + fprintf(vvp_out, "T_%u.%u ; loop end (prop)\n", thread_count, + lab_loop_end); + if (mode == 0 || mode == 1) { + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + fprintf(vvp_out, " %%pop/obj 1, 1;\n"); + } + fprintf(vvp_out, "T_%u.%u ; with end (prop)\n", thread_count, lab_end); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + 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) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + } + if (!reverse) { + fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", i_reg); + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + } else { + fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", i_reg); + fprintf(vvp_out, " %%ix/mov %u, %u;\n", i_reg, n_reg); + fprintf(vvp_out, " %%ix/sub %u, 1, 0;\n", i_reg); + } + fprintf(vvp_out, "T_%u.%u ; queue with loop (var)\n", thread_count, + lab_top); + if (!reverse) { + fprintf(vvp_out, " %%cmpix/ltu %u, %u;\n", i_reg, n_reg); + fprintf(vvp_out, " %%jmp/0 T_%u.%u, 4;\n", thread_count, + lab_loop_end); + } else { + fprintf(vvp_out, " %%cmpix/slt0 %u;\n", i_reg); + fprintf(vvp_out, " %%jmp/1 T_%u.%u, 4;\n", thread_count, + lab_loop_end); + } + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + + fprintf(vvp_out, " %%queue/word/v v%p_0, %u, %u;\n", sig, elem_wid, + i_reg); + fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", item_sig, + elem_wid); + 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); + + int pf = draw_eval_condition(pred); + fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d;\n", thread_count, lab_nom, + pf); + clr_flag(pf); + + if (mode == 0) { + fprintf(vvp_out, " %%load/vec4 v%p_0;\n", item_sig); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", append_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else if (mode == 1) { + 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 == 2) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + fprintf(vvp_out, " %%load/vec4 v%p_0;\n", item_sig); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_end); + } else if (mode == 3) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + 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_end); + } else if (mode == 4) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + fprintf(vvp_out, " %%load/vec4 v%p_0;\n", item_sig); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_end); + } else { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + 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_end); + } + + fprintf(vvp_out, "T_%u.%u ; nomatch (var)\n", thread_count, lab_nom); + if (!reverse) { + fprintf(vvp_out, " %%ix/add %u, 1, 0;\n", i_reg); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); + } else { + fprintf(vvp_out, " %%ix/sub %u, 1, 0;\n", i_reg); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); + } + + fprintf(vvp_out, "T_%u.%u ; loop end (var)\n", thread_count, + lab_loop_end); + if (mode >= 2) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + } + fprintf(vvp_out, "T_%u.%u ; with end (var)\n", thread_count, lab_end); + } + + clr_word(i_reg); + clr_word(n_reg); + return 0; +} + +static int eval_queue_method_find(ivl_expr_t expr) +{ + const char*name = ivl_expr_name(expr); + if (ivl_expr_parms(expr) == 4 && + strstr(ivl_expr_name(expr), "_with") != 0) + return eval_queue_method_find_with(expr); + + ivl_expr_t qarg = ivl_expr_parm(expr, 0); + ivl_expr_t carg = ivl_expr_parm(expr, 1); + unsigned elem_wid = queue_unique_src_elem_wid(qarg); + if (elem_wid == 0) + return 1; + + draw_eval_vec4(carg); + + if (strcmp(name, "$ivl_queue_method$find") == 0) { + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/find/prop/v %u, %u;\n", pidx, + elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + fprintf(vvp_out, " %%queue/find/v v%p_0, %u;\n", sig, + elem_wid); + } + return 0; + } + + if (strcmp(name, "$ivl_queue_method$find_index") == 0) { + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/find/index/prop/v %u, %u;\n", + pidx, elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + fprintf(vvp_out, " %%queue/find/index/v v%p_0, %u;\n", + sig, elem_wid); + } + return 0; + } + + if (strcmp(name, "$ivl_queue_method$find_first") == 0) { + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/find_first/prop/v %u, %u;\n", pidx, + elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + fprintf(vvp_out, " %%queue/find_first/v v%p_0, %u;\n", sig, + elem_wid); + } + return 0; + } + if (strcmp(name, "$ivl_queue_method$find_first_index") == 0) { + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/find_first/index/prop/v %u, %u;\n", + pidx, elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + fprintf(vvp_out, " %%queue/find_first/index/v v%p_0, %u;\n", + sig, elem_wid); + } + return 0; + } + if (strcmp(name, "$ivl_queue_method$find_last") == 0) { + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/find_last/prop/v %u, %u;\n", pidx, + elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + fprintf(vvp_out, " %%queue/find_last/v v%p_0, %u;\n", sig, + elem_wid); + } + return 0; + } + if (strcmp(name, "$ivl_queue_method$find_last_index") == 0) { + if (ivl_expr_type(qarg) == IVL_EX_PROPERTY) { + ivl_signal_t cl = ivl_expr_signal(qarg); + unsigned pidx = ivl_expr_property_idx(qarg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", cl); + fprintf(vvp_out, " %%queue/find_last/index/prop/v %u, %u;\n", + pidx, elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(qarg); + fprintf(vvp_out, " %%queue/find_last/index/v v%p_0, %u;\n", + sig, elem_wid); + } + return 0; + } + + return 1; +} + +int draw_queue_method_find_sfunc(ivl_expr_t expr) +{ + return eval_queue_method_find(expr); +} + int draw_eval_object(ivl_expr_t ex) { switch (ivl_expr_type(ex)) { @@ -288,6 +700,26 @@ int draw_eval_object(ivl_expr_t ex) case IVL_EX_UFUNC: return eval_object_ufunc(ex); + case IVL_EX_SFUNC: + /* Queue locator `with` may report IVL_VT_DARRAY in ivl; handle by name. */ + if (ivl_expr_parms(ex) == 4 && + strncmp(ivl_expr_name(ex), "$ivl_queue_method$", + sizeof("$ivl_queue_method$") - 1) == 0 && + strstr(ivl_expr_name(ex), "_with") != 0) { + if (draw_queue_method_find_sfunc(ex) == 0) + return 0; + } + if (ivl_expr_value(ex) == IVL_VT_QUEUE || + ivl_expr_value(ex) == IVL_VT_DARRAY) { + if (eval_queue_method_unique(ex) == 0) + return 0; + if (eval_queue_method_find(ex) == 0) + return 0; + } + fprintf(vvp_out, "; ERROR: draw_eval_object: unsupported IVL_EX_SFUNC " + "for object context\n"); + return 1; + default: fprintf(vvp_out, "; ERROR: draw_eval_object: Invalid expression type %d\n", ivl_expr_type(ex)); return 1; diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 73e8977b4..11b045501 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -1046,12 +1046,6 @@ static void draw_select_pad_vec4(ivl_expr_t expr) fprintf(vvp_out, " %%pad/u %u;\n", wid); } -/* - * This function handles the special case of a call to the internal - * functions $ivl_queue_method$pop_back et al. The first (and only) - * argument is the signal that represents a dynamic queue. Generate a - * %qpop instruction to pop a value and push it to the vec4 stack. - */ static void draw_darray_pop(ivl_expr_t expr) { const char*fb; @@ -1104,6 +1098,15 @@ static void draw_sfunc_vec4(ivl_expr_t expr) return; } + /* find*_with has four parameters and is lowered in eval_object.c */ + if (parm_count == 4 && + strncmp(ivl_expr_name(expr), "$ivl_queue_method$", + sizeof("$ivl_queue_method$") - 1) == 0 && + strstr(ivl_expr_name(expr), "_with") != 0) { + if (draw_queue_method_find_sfunc(expr) == 0) + return; + } + if (strcmp(ivl_expr_name(expr), "$size")==0 && parm_count==1) { ivl_expr_t arg = ivl_expr_parm(expr, 0); if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 70133a007..ae944fe4e 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -228,6 +228,12 @@ extern void draw_eval_string(ivl_expr_t ex); */ extern int draw_eval_object(ivl_expr_t ex); +/* + * Emit vvp for $ivl_queue_method$find* / find*_with when the call appears + * as a vector SFUNC (e.g. find_first with (...) returning int). + */ +extern int draw_queue_method_find_sfunc(ivl_expr_t expr); + extern int show_stmt_assign(ivl_statement_t net); extern void show_stmt_file_line(ivl_statement_t net, const char*desc); diff --git a/vvp/codes.h b/vvp/codes.h index a9d2c4844..87514e5e5 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -249,6 +249,30 @@ extern bool of_QPOP_PROP_F_REAL(vthread_t thr, vvp_code_t code); extern bool of_QPOP_PROP_F_STR(vthread_t thr, vvp_code_t code); extern bool of_QPOP_PROP_F_V(vthread_t thr, vvp_code_t code); 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_SIZE_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_WORD_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_WORD_V(vthread_t thr, vvp_code_t code); +extern bool of_CMPIX_LTU(vthread_t thr, vvp_code_t code); +extern bool of_CMPIX_SLT0(vthread_t thr, vvp_code_t code); +extern bool of_PUSH_IX_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_INDEX_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_INDEX_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_FIRST_INDEX_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_FIRST_INDEX_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_FIRST_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_FIRST_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_LAST_INDEX_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_LAST_INDEX_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_LAST_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_FIND_LAST_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_UNIQUE_INDEX_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_UNIQUE_INDEX_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_UNIQUE_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_UNIQUE_V(vthread_t thr, vvp_code_t code); extern bool of_DELETE_PROP_ELEM(vthread_t thr, vvp_code_t code); extern bool of_DELETE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_R(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index df98125ab..a40ba2714 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -139,6 +139,8 @@ static const struct opcode_table_s opcode_table[] = { { "%cmpi/ne", of_CMPINE, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%cmpi/s", of_CMPIS, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%cmpi/u", of_CMPIU, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%cmpix/ltu", of_CMPIX_LTU, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%cmpix/slt0", of_CMPIX_SLT0, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%concat/str", of_CONCAT_STR, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%concat/vec4", of_CONCAT_VEC4, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%concati/str", of_CONCATI_STR, 1,{OA_STRING,OA_NONE, OA_NONE} }, @@ -252,6 +254,7 @@ static const struct opcode_table_s opcode_table[] = { { "%prop/r", of_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/str",of_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%prop/v", of_PROP_V, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%push/ix/vec4", of_PUSH_IX_VEC4, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%pushi/real",of_PUSHI_REAL,2,{OA_BIT1, OA_BIT2, OA_NONE} }, { "%pushi/str", of_PUSHI_STR, 1,{OA_STRING, OA_NONE, OA_NONE} }, { "%pushi/vec4",of_PUSHI_VEC4,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, @@ -275,6 +278,27 @@ static const struct opcode_table_s opcode_table[] = { { "%qpop/prop/f/r", of_QPOP_PROP_F_REAL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%qpop/prop/f/str", of_QPOP_PROP_F_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%qpop/prop/f/v", of_QPOP_PROP_F_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/append_word/v", of_QUEUE_APPEND_WORD_V, 1, {OA_BIT1, OA_NONE, OA_NONE} }, + { "%queue/find/index/prop/v", of_QUEUE_FIND_INDEX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/find/index/v", of_QUEUE_FIND_INDEX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/find/prop/v", of_QUEUE_FIND_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/find/v", of_QUEUE_FIND_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/find_first/index/prop/v", of_QUEUE_FIND_FIRST_INDEX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/find_first/index/v", of_QUEUE_FIND_FIRST_INDEX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/find_first/prop/v", of_QUEUE_FIND_FIRST_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/find_first/v", of_QUEUE_FIND_FIRST_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/find_last/index/prop/v", of_QUEUE_FIND_LAST_INDEX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/find_last/index/v", of_QUEUE_FIND_LAST_INDEX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/find_last/prop/v", of_QUEUE_FIND_LAST_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/find_last/v", of_QUEUE_FIND_LAST_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/size/v", of_QUEUE_SIZE_V, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%queue/unique/index/prop/v", of_QUEUE_UNIQUE_INDEX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/unique/index/v", of_QUEUE_UNIQUE_INDEX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/unique/prop/v", of_QUEUE_UNIQUE_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/unique/v", of_QUEUE_UNIQUE_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/word/prop/v", of_QUEUE_WORD_PROP_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, + { "%queue/word/v", of_QUEUE_WORD_V, 3, {OA_FUNC_PTR, OA_BIT1, OA_BIT2} }, { "%release/net",of_RELEASE_NET,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/reg",of_RELEASE_REG,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 3bbe9d42f..96f127565 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -504,6 +504,42 @@ template static vvp_queue*get_queue_object(vthread_t thr, vvp_ return queue; } +/* + * Locator and size/word opcodes use the same VVP net for a queue ($) or a + * dynamic array ([]). Queues are vvp_queue_vec4; dynamic arrays may be + * vvp_darray_vec4, vvp_darray_atom<>, etc. (all vvp_darray, but vvp_queue + * also derives from vvp_darray — test queue vec4 first). + */ +static void get_queue_or_darray_vec4_from_net(vthread_t thr, vvp_net_t* net, + vvp_queue_vec4*& qsrc, + vvp_darray*& dsrc) +{ + vvp_fun_signal_object* obj = dynamic_cast(net->fun); + assert(obj); + vvp_object_t top = obj->get_object(); + qsrc = top.peek(); + if (qsrc) { + dsrc = 0; + return; + } + vvp_darray* dany = top.peek(); + if (dany && dynamic_cast(dany) == 0) { + dsrc = dany; + return; + } + if (top.test_nil()) { + qsrc = new vvp_queue_vec4(); + vvp_object_t val(qsrc); + vvp_net_ptr_t ptr(net, 0); + vvp_send_object(ptr, val, thr->wt_context); + dsrc = 0; + return; + } + /* e.g. real/string queue — not vec4 queue or plain darray */ + qsrc = 0; + dsrc = 0; +} + /* * The following are used to allow a common template to be written for * queue real/string/vec4 operations @@ -6211,6 +6247,464 @@ bool of_QPOP_PROP_F_V(vthread_t thr, vvp_code_t cp) return qpop_f_prop(thr, cp, cp->bit_idx[0]); } +static vvp_vector4_t queue_unique_ulong_to_vec4(unsigned long ul, unsigned bits) +{ + vvp_vector4_t val(bits, BIT4_0); + for (unsigned idx = 0; idx < bits && idx < 8 * sizeof(unsigned long); idx += 1) { + if (ul & 1UL) + val.set_bit(idx, BIT4_1); + else + val.set_bit(idx, BIT4_0); + ul >>= 1; + } + return val; +} + +template static vvp_queue_vec4* queue_run_unique_src(SRC* src, + unsigned wid, + bool as_index) +{ + vvp_queue_vec4* dst = new vvp_queue_vec4(); + if (!src) + return dst; + size_t n = src->get_size(); + for (size_t i = 0; i < n; i += 1) { + vvp_vector4_t vi(wid); + src->get_word(i, vi); + bool seen = false; + for (size_t j = 0; j < i; j += 1) { + vvp_vector4_t vj(wid); + src->get_word(j, vj); + if (vi.eeq(vj)) { + seen = true; + break; + } + } + if (seen) + continue; + if (as_index) + dst->push_back(queue_unique_ulong_to_vec4((unsigned long)i, 32), 0); + else + dst->push_back(vi, 0); + } + return dst; +} + +template +static vvp_queue_vec4* queue_run_find_matches_src(SRC* src, unsigned wid, + const vvp_vector4_t& cmp, + bool as_index) +{ + vvp_queue_vec4* dst = new vvp_queue_vec4(); + if (!src) + return dst; + size_t n = src->get_size(); + for (size_t i = 0; i < n; i += 1) { + vvp_vector4_t vi(wid); + src->get_word(i, vi); + if (vi.eeq(cmp)) { + if (as_index) + dst->push_back(queue_unique_ulong_to_vec4((unsigned long)i, 32), 0); + else + dst->push_back(vi, 0); + } + } + return dst; +} + +/* Array/queue locator methods return a queue with 0 or 1 elements (LRM). */ +template +static vvp_queue_vec4* queue_find_first_last_to_queue_src(SRC* src, + unsigned wid, + const vvp_vector4_t& cmp, + bool want_first, + bool want_index) +{ + vvp_queue_vec4* dst = new vvp_queue_vec4(); + if (!src || src->get_size() == 0) + return dst; + size_t n = src->get_size(); + if (want_first) { + for (size_t i = 0; i < n; i += 1) { + vvp_vector4_t vi(wid); + src->get_word(i, vi); + if (vi.eeq(cmp)) { + if (want_index) + dst->push_back( + queue_unique_ulong_to_vec4((unsigned long)i, 32), + 0); + else + dst->push_back(vi, 0); + return dst; + } + } + } else { + for (size_t i = n; i > 0; ) { + i -= 1; + vvp_vector4_t vi(wid); + src->get_word(i, vi); + if (vi.eeq(cmp)) { + if (want_index) + dst->push_back( + queue_unique_ulong_to_vec4((unsigned long)i, 32), + 0); + else + dst->push_back(vi, 0); + return dst; + } + } + } + return dst; +} + +bool of_QUEUE_FIND_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_run_find_matches_src(qsrc, wid, cmp, false); + else if (dsrc) + dst = queue_run_find_matches_src(dsrc, wid, cmp, false); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_run_find_matches_src(src, wid, cmp, false); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_PROP_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + if (src == 0) { + thr->push_object(vvp_object_t(new vvp_queue_vec4())); + return true; + } + vvp_queue_vec4* dst = queue_run_find_matches_src(src, wid, cmp, false); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_INDEX_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_run_find_matches_src(qsrc, wid, cmp, true); + else if (dsrc) + dst = queue_run_find_matches_src(dsrc, wid, cmp, true); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_run_find_matches_src(src, wid, cmp, true); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_INDEX_PROP_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + if (src == 0) { + thr->push_object(vvp_object_t(new vvp_queue_vec4())); + return true; + } + vvp_queue_vec4* dst = queue_run_find_matches_src(src, wid, cmp, true); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_FIRST_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_find_first_last_to_queue_src(qsrc, wid, cmp, true, false); + else if (dsrc) + dst = queue_find_first_last_to_queue_src(dsrc, wid, cmp, true, false); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_find_first_last_to_queue_src(src, wid, cmp, true, false); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_FIRST_PROP_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + vvp_queue_vec4* dst = + queue_find_first_last_to_queue_src(src, wid, cmp, true, false); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_FIRST_INDEX_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_find_first_last_to_queue_src(qsrc, wid, cmp, true, true); + else if (dsrc) + dst = queue_find_first_last_to_queue_src(dsrc, wid, cmp, true, true); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_find_first_last_to_queue_src(src, wid, cmp, true, true); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_FIRST_INDEX_PROP_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + vvp_queue_vec4* dst = + queue_find_first_last_to_queue_src(src, wid, cmp, true, true); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_LAST_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_find_first_last_to_queue_src(qsrc, wid, cmp, false, false); + else if (dsrc) + dst = queue_find_first_last_to_queue_src(dsrc, wid, cmp, false, false); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_find_first_last_to_queue_src(src, wid, cmp, false, false); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_LAST_PROP_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + vvp_queue_vec4* dst = + queue_find_first_last_to_queue_src(src, wid, cmp, false, false); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_LAST_INDEX_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_find_first_last_to_queue_src(qsrc, wid, cmp, false, true); + else if (dsrc) + dst = queue_find_first_last_to_queue_src(dsrc, wid, cmp, false, true); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_find_first_last_to_queue_src(src, wid, cmp, false, true); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_FIND_LAST_INDEX_PROP_V(vthread_t thr, vvp_code_t cp) +{ + vvp_vector4_t cmp = thr->pop_vec4(); + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + assert(cmp.size() == wid); + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + vvp_queue_vec4* dst = + queue_find_first_last_to_queue_src(src, wid, cmp, false, true); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_UNIQUE_V(vthread_t thr, vvp_code_t cp) +{ + vvp_net_t*net = cp->net; + unsigned wid = cp->bit_idx[0]; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_run_unique_src(qsrc, wid, false); + else if (dsrc) + dst = queue_run_unique_src(dsrc, wid, false); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_run_unique_src(src, wid, false); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_UNIQUE_PROP_V(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + if (src == 0) { + thr->push_object(vvp_object_t(new vvp_queue_vec4())); + return true; + } + vvp_queue_vec4* dst = queue_run_unique_src(src, wid, false); + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_UNIQUE_INDEX_V(vthread_t thr, vvp_code_t cp) +{ + vvp_net_t*net = cp->net; + unsigned wid = cp->bit_idx[0]; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_queue_vec4* dst; + if (qsrc) + dst = queue_run_unique_src(qsrc, wid, true); + else if (dsrc) + dst = queue_run_unique_src(dsrc, wid, true); + else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + dst = queue_run_unique_src(src, wid, true); + } + thr->push_object(vvp_object_t(dst)); + return true; +} + +bool of_QUEUE_UNIQUE_INDEX_PROP_V(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + if (src == 0) { + thr->push_object(vvp_object_t(new vvp_queue_vec4())); + return true; + } + vvp_queue_vec4* dst = queue_run_unique_src(src, wid, true); + thr->push_object(vvp_object_t(dst)); + return true; +} + bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t cp) { size_t pid = cp->number; @@ -6238,6 +6732,167 @@ bool of_PROP_QUEUE_SIZE(vthread_t thr, vvp_code_t cp) return true; } +bool of_QUEUE_SIZE_V(vthread_t thr, vvp_code_t cp) +{ + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + size_t sz = + qsrc ? qsrc->get_size() : (dsrc ? dsrc->get_size() : 0); + + vvp_vector4_t val(32, BIT4_0); + unsigned long ul = sz; + for (unsigned idx = 0; idx < 32; idx++) { + if (ul & 1UL) + val.set_bit(idx, BIT4_1); + else + val.set_bit(idx, BIT4_0); + ul >>= 1; + } + thr->push_vec4(val); + return true; +} + +bool of_QUEUE_WORD_V(vthread_t thr, vvp_code_t cp) +{ + unsigned wid = cp->bit_idx[0]; + unsigned ix_reg = cp->bit_idx[1]; + int64_t adr = thr->words[ix_reg].w_int; + if (thr->flags[4] == BIT4_1) { + thr->push_vec4(vvp_vector4_t(wid)); + return true; + } + if (adr < 0) { + thr->push_vec4(vvp_vector4_t(wid)); + return true; + } + vvp_net_t* net = cp->net; + vvp_queue_vec4* qsrc = 0; + vvp_darray* dsrc = 0; + get_queue_or_darray_vec4_from_net(thr, net, qsrc, dsrc); + vvp_vector4_t out(wid); + if (qsrc) { + if ((size_t)adr >= qsrc->get_size()) { + thr->push_vec4(out); + return true; + } + qsrc->get_word((unsigned)adr, out); + } else if (dsrc) { + if ((size_t)adr >= dsrc->get_size()) { + thr->push_vec4(out); + return true; + } + dsrc->get_word((unsigned)adr, out); + } else { + vvp_queue* src_q = get_queue_object(thr, net); + vvp_queue_vec4* src = dynamic_cast(src_q); + if (!src || (size_t)adr >= src->get_size()) { + thr->push_vec4(out); + return true; + } + src->get_word((unsigned)adr, out); + } + thr->push_vec4(out); + return true; +} + +bool of_QUEUE_WORD_PROP_V(vthread_t thr, vvp_code_t cp) +{ + size_t pid = cp->number; + unsigned wid = cp->bit_idx[0]; + unsigned ix_reg = cp->bit_idx[1]; + int64_t adr = thr->words[ix_reg].w_int; + if (thr->flags[4] == BIT4_1) { + thr->push_vec4(vvp_vector4_t(wid)); + return true; + } + if (adr < 0) { + thr->push_vec4(vvp_vector4_t(wid)); + return true; + } + + vvp_object_t& top = thr->peek_object(); + vvp_cobject*cobj = top.peek(); + assert(cobj); + + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + vvp_queue_vec4* src = qobj.peek(); + vvp_vector4_t out(wid); + if (src == 0 || (size_t)adr >= src->get_size()) { + thr->push_vec4(out); + return true; + } + src->get_word((unsigned)adr, out); + thr->push_vec4(out); + return true; +} + +bool of_CMPIX_LTU(vthread_t thr, vvp_code_t cp) +{ + unsigned a = cp->number; + unsigned b = cp->bit_idx[0]; + if (thr->words[a].w_uint < thr->words[b].w_uint) + thr->flags[4] = BIT4_1; + else + thr->flags[4] = BIT4_0; + return true; +} + +bool of_CMPIX_SLT0(vthread_t thr, vvp_code_t cp) +{ + unsigned r = cp->number; + thr->flags[4] = (thr->words[r].w_int < 0) ? BIT4_1 : BIT4_0; + return true; +} + +bool of_PUSH_IX_VEC4(vthread_t thr, vvp_code_t cp) +{ + unsigned ix = cp->number; + unsigned wid = cp->bit_idx[0]; + unsigned sgn = cp->bit_idx[1]; + uint32_t ubits; + if (sgn) { + int32_t sv = (int32_t)thr->words[ix].w_int; + ubits = (uint32_t)sv; + } else { + ubits = (uint32_t)thr->words[ix].w_uint; + } + if (wid > 32) + wid = 32; + vvp_vector4_t tmp(wid, BIT4_0); + for (unsigned idx = 0; idx < wid; idx++) { + if (ubits & 1u) + tmp.set_bit(idx, BIT4_1); + else + tmp.set_bit(idx, BIT4_0); + ubits >>= 1; + } + thr->push_vec4(tmp); + return true; +} + +bool of_QUEUE_NEW_EMPTY_V(vthread_t thr, vvp_code_t) +{ + thr->push_object(vvp_object_t(new vvp_queue_vec4())); + return true; +} + +bool of_QUEUE_APPEND_WORD_V(vthread_t thr, vvp_code_t cp) +{ + unsigned wid = cp->bit_idx[0]; + vvp_vector4_t elem = thr->pop_vec4(); + assert(elem.size() == wid); + vvp_object_t qobj; + thr->pop_object(qobj); + vvp_queue_vec4* q = qobj.peek(); + assert(q); + q->push_back(elem, 0); + thr->push_object(qobj); + return true; +} + bool of_DELETE_PROP_ELEM(vthread_t thr, vvp_code_t cp) { size_t pid = cp->number;