diff --git a/.gitignore b/.gitignore index 319b3c199..7343305f2 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ stamp-*-h /version_tag.h # Directories +/devel/ autom4te.cache dep diff --git a/PExpr.cc b/PExpr.cc index 8a32b0506..8aa61caf1 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -261,12 +261,37 @@ PECallFunction::PECallFunction(perm_string n, const list &parms) { } +PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const vector &parms) +: path_(method), parms_(parms), chain_prefix_(chain_prefix), is_overridden_(false) +{ +} + +PECallFunction::PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const list &parms) +: path_(method), parms_(parms.begin(), parms.end()), + chain_prefix_(chain_prefix), is_overridden_(false) +{ +} + +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); @@ -275,12 +300,15 @@ void PECallFunction::declare_implicit_nets(LexicalScope*scope, NetNet::Type type bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const { - bool flag = false; + 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) - flag |= parm.parm->has_aa_term(des, scope); + if (parm.parm && parm.parm->has_aa_term(des, scope)) + return true; } - return flag; + return false; } PEConcat::PEConcat(const list&p, PExpr*r) diff --git a/PExpr.h b/PExpr.h index 78c3055bb..99c1df05d 100644 --- a/PExpr.h +++ b/PExpr.h @@ -37,6 +37,7 @@ class NetExpr; class NetScope; class PPackage; struct symbol_search_results; +class netclass_t; /* * The PExpr class hierarchy supports the description of @@ -924,8 +925,22 @@ class PECallFunction : public PExpr { explicit PECallFunction(const pform_name_t &n, const std::list &parms); explicit PECallFunction(perm_string n, const std::list &parms); + // SystemVerilog: prefix().method(args) — prefix elaborates to a class handle. + explicit PECallFunction(PExpr* chain_prefix, const pform_name_t &method, + const std::vector &parms); + 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). + const pform_scoped_name_t& peek_path(void) const { return path_; } + const PExpr* peek_chain_prefix(void) const { return chain_prefix_; } + virtual void dump(std::ostream &) const override; virtual void declare_implicit_nets(LexicalScope*scope, NetNet::Type type) override; @@ -944,6 +959,9 @@ class PECallFunction : public PExpr { private: pform_scoped_name_t path_; 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_; @@ -981,7 +999,26 @@ class PECallFunction : public PExpr { unsigned elaborate_arguments_(Design*des, NetScope*scope, const NetFuncDef*def, bool need_const, std::vector&parms, - unsigned parm_off) const; + unsigned parm_off, + const std::vector*src_parms = nullptr) const; + + NetExpr* elaborate_class_method_net_(Design*des, NetScope*scope, + NetNet*net, const netclass_t*class_type, + perm_string method_name, + const std::vector*src_parms) const; + + NetExpr* elaborate_class_method_net_this_(Design*des, NetScope*scope, + NetExpr* this_expr, + const netclass_t*class_type, + perm_string method_name, + const std::vector*src_parms) const; + + NetExpr* elaborate_expr_method_chained_(Design*des, NetScope*scope, + symbol_search_results&search_results) const; + + NetExpr* elaborate_expr_chain_(Design*des, NetScope*scope, unsigned flags) const; + + unsigned test_width_chain_(Design*des, NetScope*scope, width_mode_t&mode); }; /* diff --git a/Statement.h b/Statement.h index d410caaef..4229dfa0d 100644 --- a/Statement.h +++ b/Statement.h @@ -257,17 +257,17 @@ class PCallTask : public Statement { NetProc*elaborate_build_call_(Design*des, NetScope*scope, NetScope*task, NetExpr*use_this) const; NetProc*elaborate_sys_task_method_(Design*des, NetScope*scope, - NetNet*net, + NetExpr*base_expr, perm_string method_name, const char *sys_task_name, const std::vector &parm_names = {}) const; NetProc*elaborate_queue_method_(Design*des, NetScope*scope, - NetNet*net, + NetExpr*queue_base, perm_string method_name, const char *sys_task_name, const std::vector &parm_names) const; NetProc*elaborate_method_func_(NetScope*scope, - NetNet*net, + NetExpr*queue_base, ivl_type_t type, perm_string method_name, const char*sys_task_name) const; diff --git a/devel/sv_call_chain.md b/devel/sv_call_chain.md new file mode 100644 index 000000000..fa0f73e0d --- /dev/null +++ b/devel/sv_call_chain.md @@ -0,0 +1,72 @@ +# SystemVerilog chained calls: `a().b()` + +This note describes the parser and elaboration support for **chained calls**: a +function call whose value is a class handle, followed by one or more +`.method(args)` segments (e.g. `get_c().f()`, `a().b().c()`). + +## Language shape + +- **Parse:** A dedicated nonterminal `call_chain_expr` in `parse.y` builds a + left-associated chain: + - `hierarchy_identifier attribute_list_opt argument_list_parens` — first call; + - `call_chain_expr '.' hierarchy_identifier attribute_list_opt argument_list_parens` — each further segment. +- **`expr_primary`** includes `call_chain_expr` **before** the bare + `hierarchy_identifier` alternative so `id (` is parsed as a call, not as an + identifier plus a stray `(`. + +## Parse tree (`PExpr`) + +- **`PECallFunction`** (`PExpr.h` / `PExpr.cc`): + - Optional **`chain_prefix_`**: inner `PExpr` for the prefix (another + `PECallFunction` for longer chains). + - Constructors and accessors: `peek_path()`, `peek_chain_prefix()`. +- **`pform_make_chained_call_function`** (`pform.cc`) — requires SystemVerilog; + builds `PECallFunction(prefix, method_name, args)`. + +## Elaboration (`elab_expr.cc`) + +- **`PECallFunction::elaborate_expr_`** delegates to **`elaborate_expr_chain_`** + when `chain_prefix_` is set. +- **`elaborate_class_method_net_this_`** passes the elaborated prefix as the + implicit `this` argument (first parameter slot), including nested `NetEUFunc` + for inner calls — not only `NetESignal(net)`. +- **`resolve_call_chain_prefix_class`** (static helper) resolves the **class + type** of the prefix for multi-hop chains (e.g. width checks), walking the + chain prefix recursively instead of searching only the tail name. + +## Dump / debug + +- **`pform_dump.cc`** prints chained calls with a `prefix.` prefix before the + method path. + +## Regression + +- **`ivtest/ivltests/sv_call_chain_method1.v`** +- **`ivtest/vvp_tests/sv_call_chain_method1.json`** (`-g2012`) +- Listed in **`ivtest/regress-vvp.list`** as `sv_call_chain_method1`. + +## Using a locally built `iverilog` + +`iverilog` invokes the installed compiler under your prefix, typically +`$PREFIX/lib/ivl/ivl`, not the `ivl` binary in the build tree. After changing +the parser, **reinstall** or copy the new `ivl` into that lib directory so +`iverilog` picks up the change; otherwise chained-call syntax may still fail +with a **syntax error** while a direct `./ivl -C...` test from the build tree +succeeds. + +```bash +# Example after building in-tree +make install +# or copy only the compiler binary to your existing install +cp ivl "$PREFIX/lib/ivl/ivl" +``` + +## Related files (non-exhaustive) + +| Area | Files | +|------------|--------| +| Grammar | `parse.y` (`call_chain_expr`, `expr_primary`) | +| Parse form | `pform.cc`, `pform.h` | +| AST | `PExpr.h`, `PExpr.cc` | +| Elab | `elab_expr.cc` | +| Dump | `pform_dump.cc` | 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 671381e62..e92f34d71 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -41,9 +41,117 @@ # 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 = 0; + if (method_suffix == "sum" || method_suffix == "product") + pred = elab_and_eval(des, ws, with_expr, (int)ew, false, false, + ivl_type_base(element_type)); + else + 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 if (method_suffix == "unique") + sfunc_name = lex_strings.make("$ivl_queue_method$unique_with"); + else if (method_suffix == "unique_index") + sfunc_name = lex_strings.make("$ivl_queue_method$unique_index_with"); + else if (method_suffix == "min") + sfunc_name = lex_strings.make("$ivl_queue_method$min_with"); + else if (method_suffix == "max") + sfunc_name = lex_strings.make("$ivl_queue_method$max_with"); + else if (method_suffix == "sum") + sfunc_name = lex_strings.make("$ivl_queue_method$sum_with"); + else if (method_suffix == "product") + sfunc_name = lex_strings.make("$ivl_queue_method$product_with"); + else + ivl_assert(loc, 0); + + 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) { @@ -55,6 +163,53 @@ bool type_is_vectorable(ivl_variable_type_t type) } } +/* + * For a().b()… call chains, find the class type of the prefix expression + * (the value before the final ".method(args)"). + */ +static const netclass_t* resolve_call_chain_prefix_class(Design*des, NetScope*scope, + PECallFunction* link) +{ + if (link == 0) + return 0; + + if (link->peek_chain_prefix() == 0) { + symbol_search_results sr; + if (!symbol_search(link, des, scope, link->peek_path(), UINT_MAX, &sr)) + return 0; + if (!sr.is_scope() || sr.scope->type() != NetScope::FUNC) + return 0; + NetFuncDef* fundef = sr.scope->func_def(); + ivl_assert(*link, fundef); + NetScope* dscope = fundef->scope(); + NetNet* res = dscope->find_signal(dscope->basename()); + if (res == 0) + return 0; + return dynamic_cast(res->net_type()); + } + + PECallFunction* inner = dynamic_cast( + const_cast(link->peek_chain_prefix())); + if (inner == 0) + return 0; + + const netclass_t* recv_class = resolve_call_chain_prefix_class(des, scope, inner); + if (recv_class == 0) + return 0; + + perm_string mname = peek_tail_name(link->peek_path()); + NetScope* mscope = recv_class->method_from_name(mname); + if (mscope == 0) + return 0; + NetFuncDef* mdef = mscope->func_def(); + if (mdef == 0) + return 0; + NetNet* mres = mscope->find_signal(mscope->basename()); + if (mres == 0) + return 0; + return dynamic_cast(mres->net_type()); +} + static ivl_nature_t find_access_function(const pform_scoped_name_t &path) { if (path.package || path.name.size() != 1) @@ -1537,12 +1692,39 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, << "search_results.net->net_type: " << *search_results.net->net_type() << endl; } - // Don't support multiple chained methods yet. + // Chained class methods: obj.m1().m2() — width is that of the last call. if (search_results.path_tail.size() > 1) { + if (search_results.net && search_results.net->data_type()==IVL_VT_CLASS) { + const netclass_t* cur_class = dynamic_cast(search_results.type); + if (cur_class) { + size_t ntail = search_results.path_tail.size(); + size_t idx = 0; + for (auto it = search_results.path_tail.begin() + ; it != search_results.path_tail.end() ; ++it, ++idx) { + perm_string mname = it->name; + NetScope*meth = cur_class->method_from_name(mname); + if (meth == 0) + return 0; + const NetNet*res = meth->find_signal(meth->basename()); + if (res == 0) + return 0; + if (idx + 1 == ntail) { + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + return expr_width_; + } + cur_class = dynamic_cast(res->net_type()); + if (cur_class == 0) + return 0; + } + } + } if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width_method_: " << "Chained path tail (" << search_results.path_tail - << ") not supported." << endl; + << ") not supported for this expression type." << endl; } return 0; } @@ -1571,6 +1753,47 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } + if (method_name == "sum") { + 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 == "product") { + 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" || method_name == "find_index" || + method_name == "unique" || method_name == "unique_index" || + method_name == "min" || method_name == "max") { + 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; } @@ -1602,6 +1825,54 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return expr_width_; } + if (method_name == "sum") { + 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 == "product") { + 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 == "unique" || method_name == "unique_index" || + method_name == "min" || method_name == "max") { + 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; } @@ -1715,9 +1986,53 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*, return 0; } +unsigned PECallFunction::test_width_chain_(Design*des, NetScope*scope, + width_mode_t&) +{ + PECallFunction* inner_pf = dynamic_cast(chain_prefix_); + if (inner_pf == 0) { + expr_width_ = 0; + return 0; + } + + const netclass_t* cls = resolve_call_chain_prefix_class(des, scope, inner_pf); + if (cls == 0) { + expr_width_ = 0; + return 0; + } + + perm_string mname = peek_tail_name(path_); + NetScope* mscope = cls->method_from_name(mname); + if (mscope == 0) { + expr_width_ = 0; + return 0; + } + + NetFuncDef* mdef = mscope->func_def(); + if (mdef == 0 || mdef->is_void()) { + expr_width_ = 0; + return 0; + } + + NetNet* mres = mscope->find_signal(mscope->basename()); + if (mres == 0) { + expr_width_ = 0; + return 0; + } + + expr_type_ = mres->data_type(); + expr_width_ = mres->vector_width(); + min_width_ = expr_width_; + signed_flag_ = mres->get_signed(); + return expr_width_; +} + unsigned PECallFunction::test_width(Design*des, NetScope*scope, width_mode_t&mode) { + if (chain_prefix_) + return test_width_chain_(des, scope, mode); + if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width: " << "path_: " << path_ << endl; @@ -2729,7 +3044,6 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { - const netclass_t *class_type = dynamic_cast(sr.type); const name_component_t comp = sr.path_tail.front(); @@ -2807,6 +3121,26 @@ NetExpr* PEIdent::elaborate_expr_class_field_(Design*des, NetScope*scope, canon_index = make_canonical_index(des, scope, this, comp.index, tmp_ua, false); } + } else if (dynamic_cast(tmp_type)) { + /* Queue or dynamic-array property: optional index, e.g. c.q[i] + * or whole-container reference c.q. */ + const std::list*idx_list = &comp.index; + if (idx_list->empty() && !path_.back().index.empty()) + idx_list = &path_.back().index; + if (idx_list->size() == 0) { + canon_index = nullptr; + } else if (idx_list->size() != 1) { + cerr << get_fileline() << ": error: " + << "Got " << idx_list->size() << " indices, " + << "expecting 0 or 1 for the queue/dynamic-array property " + << class_type->get_prop_name(pidx) << "." << endl; + des->errors++; + } else { + const index_component_t&use_index = idx_list->back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + canon_index = elab_and_eval(des, scope, use_index.msb, -1, false); + } } if (debug_elaborate && canon_index) { @@ -2845,6 +3179,9 @@ NetExpr* PECallFunction::elaborate_expr_(Design*des, NetScope*scope, { flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + if (chain_prefix_) + return elaborate_expr_chain_(des, scope, flags); + // Search for the symbol. This should turn up a scope. symbol_search_results search_results; bool search_flag = symbol_search(this, des, scope, path_, UINT_MAX, &search_results); @@ -3125,13 +3462,16 @@ NetExpr* PECallFunction::elaborate_base_(Design*des, NetScope*scope, NetScope*ds unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, const NetFuncDef*def, bool need_const, vector&parms, - unsigned parm_off) const + unsigned parm_off, + const vector*src_parms) const { unsigned parm_errors = 0; unsigned missing_parms = 0; + const vector&use_parms = src_parms ? *src_parms : parms_; + const unsigned parm_count = parms.size() - parm_off; - const unsigned actual_count = parms_.size(); + const unsigned actual_count = use_parms.size(); if (parm_count == 0 && actual_count == 0) return 0; @@ -3144,7 +3484,7 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, des->errors += 1; } - auto args = map_named_args(des, def, parms_, parm_off); + auto args = map_named_args(des, def, use_parms, parm_off); for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { unsigned pidx = idx + parm_off; @@ -3199,6 +3539,192 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, return parm_errors; } +/* + * Elaborate a call to a single class method, optionally using an alternate + * argument vector (for chained calls where inner methods use no user args). + */ +NetExpr* PECallFunction::elaborate_class_method_net_(Design*des, NetScope*scope, + NetNet*net, const netclass_t*class_type, + perm_string method_name, + const vector*src_parms) const +{ + NetESignal*ethis = new NetESignal(net); + ethis->set_line(*this); + return elaborate_class_method_net_this_(des, scope, ethis, class_type, + method_name, src_parms); +} + +NetExpr* PECallFunction::elaborate_class_method_net_this_(Design*des, NetScope*scope, + NetExpr* this_expr, + const netclass_t*class_type, + perm_string method_name, + const vector*src_parms) const +{ + NetScope*method = class_type->method_from_name(method_name); + + if (method == 0) { + cerr << get_fileline() << ": Error: " << method_name + << " is not a method of class " << class_type->get_name() + << "." << endl; + des->errors += 1; + return 0; + } + + const NetFuncDef*def = method->func_def(); + ivl_assert(*this, def); + + NetNet*res = method->find_signal(method->basename()); + ivl_assert(*this, res); + + vector parms(def->port_count()); + ivl_assert(*this, def->port_count() >= 1); + + parms[0] = this_expr; + + elaborate_arguments_(des, scope, def, false, parms, 1, src_parms); + + NetESignal*eres = new NetESignal(res); + NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); + call->set_line(*this); + return call; +} + +NetExpr* PECallFunction::elaborate_expr_chain_(Design*des, NetScope*scope, + unsigned flags) const +{ + ivl_assert(*this, chain_prefix_); + NetExpr* inner = chain_prefix_->elaborate_expr(des, scope, -1, flags); + if (inner == 0) + return 0; + + const netclass_t* cls = dynamic_cast(inner->net_type()); + if (cls == 0) { + cerr << get_fileline() << ": error: " + << "The prefix of a chained call (a().b()) must be an expression " + << "with class type." << endl; + des->errors += 1; + delete inner; + return 0; + } + + perm_string method_name = peek_tail_name(path_); + return elaborate_class_method_net_this_(des, scope, inner, cls, + method_name, &parms_); +} + +/* + * Handle obj.m1().m2(args): arguments apply only to the last call; intermediate + * methods must be class methods returning class handles. + */ +NetExpr* PECallFunction::elaborate_expr_method_chained_(Design*des, NetScope*scope, + symbol_search_results&search_results) const +{ + static const vector no_parms; + + if (search_results.par_val && search_results.type) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting is not supported for parameter methods yet." << endl; + des->errors += 1; + return 0; + } + + NetExpr* sub_expr = 0; + if (search_results.net) { + NetESignal*tmp = new NetESignal(search_results.net); + tmp->set_line(*this); + sub_expr = tmp; + } + + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE + && search_results.path_head.back().index.size()==1) { + + const NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + const index_component_t&use_index = search_results.path_head.back().index.back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + + NetExpr*mux = elab_and_eval(des, scope, use_index.msb, -1, false); + if (!mux) + return 0; + + NetESelect*tmp = new NetESelect(sub_expr, mux, darray->element_width(), darray->element_type()); + tmp->set_line(*this); + sub_expr = tmp; + } + + if (!sub_expr) { + cerr << get_fileline() << ": internal error: " + << "Method chain elaborate lost base sub-expression." << endl; + des->errors += 1; + return 0; + } + + if (sub_expr->expr_type() != IVL_VT_CLASS) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting for this expression type is not supported yet " + << "(only class handle chains)." << endl; + cerr << get_fileline() << ": : " + << "method path: " << search_results.path_tail << endl; + des->errors += 1; + return 0; + } + + NetNet* cur_net = search_results.net; + const netclass_t* cur_class = dynamic_cast(search_results.type); + if (cur_class == 0) + cur_class = dynamic_cast(cur_net->net_type()); + if (cur_class == 0) { + cerr << get_fileline() << ": internal error: " + << "IVL_VT_CLASS net without netclass_t type." << endl; + des->errors += 1; + return 0; + } + + size_t chain_len = search_results.path_tail.size() - 1; + size_t step_idx = 0; + for (auto it = search_results.path_tail.begin() + ; step_idx < chain_len ; ++it, ++step_idx) { + perm_string method_name = it->name; + + NetExpr* step = elaborate_class_method_net_(des, scope, cur_net, cur_class, + method_name, &no_parms); + if (step == 0) + return 0; + + NetEUFunc*uf = dynamic_cast (step); + if (uf == 0) { + cerr << get_fileline() << ": internal error: " + << "expected class method call to be NetEUFunc." << endl; + des->errors += 1; + return 0; + } + + const NetESignal*rs = uf->result_sig(); + ivl_assert(*this, rs); + cur_net = const_cast(rs->sig()); + ivl_assert(*this, cur_net); + cur_class = dynamic_cast(cur_net->net_type()); + if (cur_class == 0) { + cerr << get_fileline() << ": sorry: " + << "Method chaining requires intermediate results to be class handles; " + << "after `" << method_name << "` the type is not a class." << endl; + des->errors += 1; + return 0; + } + } + + symbol_search_results tail_sr; + tail_sr.scope = search_results.scope; + tail_sr.path_head = search_results.path_head; + tail_sr.path_tail.clear(); + tail_sr.path_tail.push_back(search_results.path_tail.back()); + tail_sr.net = cur_net; + tail_sr.type = cur_class; + + return elaborate_expr_method_(des, scope, tail_sr); +} + /* * Look for a method of a given object. The search_results gives us the * information we need to look into this case: The net is the object that will @@ -3222,12 +3748,794 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, return 0; } + // e.g. c.q.size() — path_tail is {q, size}; q is a queue-typed property + if (search_results.path_tail.size() == 2) { + const netclass_t*cls = dynamic_cast(search_results.type); + if (!cls && search_results.net && search_results.net->net_type()) + cls = dynamic_cast(search_results.net->net_type()); + if (cls && search_results.net) { + perm_string prop_name = search_results.path_tail.front().name; + int pidx = cls->property_idx_from_name(prop_name); + if (pidx >= 0) { + ivl_type_t ptype = cls->get_prop_type(pidx); + if (ptype && dynamic_cast(ptype)) { + NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); + prop->set_line(*this); + perm_string method_name = search_results.path_tail.back().name; + const netqueue_t*queue = dynamic_cast(ptype); + ivl_assert(*this, queue); + ivl_type_t element_type = queue->element_type(); + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "pop_back") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_back() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_back", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "pop_front") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_front() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_front", + element_type, 1); + sys_expr->set_line(*this); + 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; + } + 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); + } + 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; + } + 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); + } + 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 == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "product") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: product() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue product() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$product", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "min" || method_name == "max") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue " << method_name + << "() 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); + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + static_cast(queue), 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 || + ptype->base_type() == IVL_VT_QUEUE)) { + NetEProperty*prop = new NetEProperty(search_results.net, pidx, nullptr); + prop->set_line(*this); + perm_string method_name = search_results.path_tail.back().name; + ivl_type_t element_type = ivl_type_element(ptype); + ivl_type_t array_rtype = ptype; + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1); + sys_expr->set_line(*this); + 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: array unique() 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, array_rtype, method_name); + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$unique", array_rtype, 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: array unique_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); + } + 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: 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_, prop, + element_type, array_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", array_rtype, 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: 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_, 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: 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_, prop, + element_type, array_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", array_rtype, 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: 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_, 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: 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_, prop, + element_type, array_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", array_rtype, 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: 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_, 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 (method_name == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: array sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "product") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: product() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: array product() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$product", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + if (method_name == "min" || method_name == "max") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: array " << method_name + << "() 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, array_rtype, method_name); + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + array_rtype, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, prop); + return sys_expr; + } + } + } + } + } + if (search_results.path_tail.size() > 1) { - cerr << get_fileline() << ": sorry: " - << "Method name nesting is not supported yet." << endl; - cerr << get_fileline() << ": : " - << "method path: " << search_results.path_tail << endl; - return 0; + return elaborate_expr_method_chained_(des, scope, search_results); } if (debug_elaborate) { @@ -3316,6 +4624,346 @@ 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 == "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: dynamic array unique() 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); + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$unique", queue_rtype, 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: dynamic array unique_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); + } + 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: 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; + } + if (method_name == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, + element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + if (method_name == "product") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: product() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, + element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array product() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$product", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + if (method_name == "min" || method_name == "max") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array " << method_name + << "() 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); + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + queue_rtype, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + cerr << get_fileline() << ": error: Method " << method_name << " is not a dynamic array method." << endl; return 0; @@ -3368,6 +5016,344 @@ 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; + } + 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); + } + 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; + } + 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); + } + 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 == "sum") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: sum() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, + element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$sum", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + if (method_name == "product") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: product() method " + << "takes no arguments" << endl; + des->errors += 1; + } + 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, + element_type, method_name); + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue product() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*sys_expr = new NetESFunc( + "$ivl_queue_method$product", + element_type, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + if (method_name == "min" || method_name == "max") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: " << method_name + << "() method takes no arguments" << endl; + des->errors += 1; + } + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue " << method_name + << "() 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); + } + NetESFunc*sys_expr = new NetESFunc( + method_name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + static_cast(queue), 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; @@ -3391,41 +5377,13 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, // Class methods. Generate function call to the class method. if (sub_expr->expr_type()==IVL_VT_CLASS) { - // Get the method name that we are looking for. perm_string method_name = search_results.path_tail.back().name; NetNet*net = search_results.net; const netclass_t*class_type = dynamic_cast(search_results.type); ivl_assert(*this, class_type); - NetScope*method = class_type->method_from_name(method_name); - - if (method == 0) { - cerr << get_fileline() << ": Error: " << method_name - << " is not a method of class " << class_type->get_name() - << "." << endl; - des->errors += 1; - return 0; - } - - const NetFuncDef*def = method->func_def(); - ivl_assert(*this, def); - - NetNet*res = method->find_signal(method->basename()); - ivl_assert(*this, res); - - vector parms(def->port_count()); - ivl_assert(*this, def->port_count() >= 1); - - NetESignal*ethis = new NetESignal(net); - ethis->set_line(*this); - parms[0] = ethis; - - elaborate_arguments_(des, scope, def, false, parms, 1); - - NetESignal*eres = new NetESignal(res); - NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); - call->set_line(*this); - return call; + return elaborate_class_method_net_(des, scope, net, class_type, + method_name, nullptr); } // String methods. @@ -4559,9 +6517,134 @@ 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; + } + if (member_comp.name == "sum") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$sum", + 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 == "product") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue product() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$product", + 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 == "min" || member_comp.name == "max") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue " << member_comp.name + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + member_comp.name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + 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; + } + 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; @@ -4778,9 +6861,132 @@ 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; + } + if (member_comp.name == "sum") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + "$ivl_queue_method$sum", + 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 == "min" || member_comp.name == "max") { + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: queue " << member_comp.name + << "() for this element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc( + member_comp.name == "min" ? "$ivl_queue_method$min" + : "$ivl_queue_method$max", + 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; + } + } + + // 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 @@ -4801,40 +7007,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") { @@ -4863,17 +7045,39 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope, return 0; // FIXME: Check this is a real or integral type. } else if (member_comp.name == "sum") { - cerr << get_fileline() << ": sorry: 'sum()' " - "array reduction method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; + const netdarray_t* dar_sum = sr.net->darray_type(); + ivl_assert(*this, dar_sum); + ivl_type_t element_type = dar_sum->element_type(); + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array sum() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc("$ivl_queue_method$sum", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; } else if (member_comp.name == "product") { - cerr << get_fileline() << ": sorry: 'product()' " - "array reduction method is not currently " - "implemented." << endl; - des->errors += 1; - return 0; + const netdarray_t* dar_sum = sr.net->darray_type(); + ivl_assert(*this, dar_sum); + ivl_type_t element_type = dar_sum->element_type(); + if (!queue_method_element_is_integral_vec4(element_type)) { + cerr << get_fileline() << ": sorry: dynamic array product() for this " + << "element type is not yet supported." << endl; + des->errors += 1; + return 0; + } + NetESFunc*fun = new NetESFunc("$ivl_queue_method$product", + element_type, 1); + fun->set_line(*this); + NetESignal*arg = new NetESignal(sr.net); + arg->set_line(*sr.net); + fun->parm(0, arg); + return fun; // FIXME: Check this is only an integral type. } else if (member_comp.name == "and") { cerr << get_fileline() << ": sorry: 'and()' " @@ -4896,45 +7100,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/elab_sig.cc b/elab_sig.cc index 5b8ed476b..19fa97a42 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -397,11 +397,6 @@ void netclass_t::elaborate_sig(Design*des, PClass*pclass) << " type=" << *use_type << endl; } - if (dynamic_cast (use_type)) { - cerr << cur->second.get_fileline() << ": sorry: " - << "Queues inside classes are not yet supported." << endl; - des->errors++; - } set_property(cur->first, cur->second.qual, use_type); if (! cur->second.qual.test_static()) diff --git a/elaborate.cc b/elaborate.cc index 6839156da..8658f6fab 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -47,6 +47,7 @@ # include "netenum.h" # include "netvector.h" # include "netdarray.h" +# include "netqueue.h" # include "netparray.h" # include "netscalar.h" # include "netclass.h" @@ -3683,22 +3684,30 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const * sys_task_name is the internal system-task name to use. */ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, - NetNet*net, + NetExpr*base_expr, perm_string method_name, const char *sys_task_name, const std::vector &parm_names) const { - NetESignal*sig = new NetESignal(net); - sig->set_line(*this); + base_expr->set_line(*this); unsigned nparms = parms_.size(); vectorargv (1 + nparms); - argv[0] = sig; + argv[0] = base_expr; if (method_name == "delete") { // The queue delete method takes an optional element. - if (net->queue_type()) { + bool is_queue = false; + if (const NetESignal*ns = dynamic_cast(base_expr)) { + is_queue = ns->sig()->queue_type() != 0; + } else if (const NetEProperty*np = dynamic_cast(base_expr)) { + const netclass_t*ct = dynamic_cast(np->get_sig()->net_type()); + ivl_assert(*this, ct); + ivl_type_t pt = ct->get_prop_type(np->property_idx()); + is_queue = pt && pt->base_type() == IVL_VT_QUEUE; + } + if (is_queue) { if (nparms > 1) { cerr << get_fileline() << ": error: queue delete() " << "method takes zero or one argument." << endl; @@ -3732,13 +3741,25 @@ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope, * sys_task_name is the internal system-task name to use. */ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, - NetNet*net, + NetExpr*queue_base, perm_string method_name, const char *sys_task_name, const std::vector &parm_names) const { - NetESignal*sig = new NetESignal(net); - sig->set_line(*this); + queue_base->set_line(*this); + + const netdarray_t*use_darray = 0; + if (const NetESignal*ns = dynamic_cast(queue_base)) { + use_darray = ns->sig()->darray_type(); + } else if (const NetEProperty*np = dynamic_cast(queue_base)) { + const netclass_t*ct = dynamic_cast(np->get_sig()->net_type()); + ivl_assert(*this, ct); + ivl_type_t pt = ct->get_prop_type(np->property_idx()); + use_darray = dynamic_cast(pt); + ivl_assert(*this, use_darray); + } else { + ivl_assert(*this, 0); + } unsigned nparms = parms_.size(); // insert() requires two arguments. @@ -3755,19 +3776,19 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, } // Get the context width if this is a logic type. - ivl_variable_type_t base_type = net->darray_type()->element_base_type(); + ivl_variable_type_t base_type = use_darray->element_base_type(); int context_width = -1; switch (base_type) { case IVL_VT_BOOL: case IVL_VT_LOGIC: - context_width = net->darray_type()->element_width(); + context_width = use_darray->element_width(); break; default: break; } vectorargv (nparms+1); - argv[0] = sig; + argv[0] = queue_base; auto args = map_named_args(des, parm_names, parms_); if (method_name != "insert") { @@ -3811,7 +3832,7 @@ NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, * This is used for array/queue function methods called as tasks. */ NetProc* PCallTask::elaborate_method_func_(NetScope*scope, - NetNet*net, + NetExpr*queue_base, ivl_type_t type, perm_string method_name, const char*sys_task_name) const @@ -3824,9 +3845,8 @@ NetProc* PCallTask::elaborate_method_func_(NetScope*scope, // Generate the function. NetESFunc*sys_expr = new NetESFunc(sys_task_name, type, 1); sys_expr->set_line(*this); - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - sys_expr->parm(0, arg); + queue_base->set_line(*this); + sys_expr->parm(0, queue_base); // Create a L-value that matches the function return type. NetNet*tmp; tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, type); @@ -3886,46 +3906,179 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, << net->name() << ".data_type() --> " << net->data_type() << endl; } + // Class handle with one path_tail component: queue/darray property method + // e.g. c.q.push_back(x) => sr.path_tail = {q}, method_name = push_back + if (sr.path_tail.size() == 1) { + const netclass_t*cls = dynamic_cast(sr.type); + if (!cls && net->net_type()) + cls = dynamic_cast(net->net_type()); + if (cls) { + perm_string prop_name = sr.path_tail.front().name; + int pidx = cls->property_idx_from_name(prop_name); + if (pidx >= 0) { + ivl_type_t ptype = cls->get_prop_type(pidx); + if (ptype && dynamic_cast(ptype)) { + NetEProperty*prop = new NetEProperty(net, pidx, 0); + prop->set_line(*this); + const netdarray_t*use_darray = dynamic_cast(ptype); + ivl_assert(*this, use_darray); + + if (method_name == "push_back") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_method_(des, scope, prop, method_name, + "$ivl_queue_method$push_back", + parm_names); + } + if (method_name == "push_front") { + static const std::vector parm_names = { + perm_string::literal("item") + }; + return elaborate_queue_method_(des, scope, prop, method_name, + "$ivl_queue_method$push_front", + parm_names); + } + if (method_name == "insert") { + static const std::vector parm_names = { + perm_string::literal("index"), + perm_string::literal("item") + }; + return elaborate_queue_method_(des, scope, prop, method_name, + "$ivl_queue_method$insert", + parm_names); + } + if (method_name == "pop_front") { + return elaborate_method_func_(scope, prop, + use_darray->element_type(), + method_name, + "$ivl_queue_method$pop_front"); + } + if (method_name == "pop_back") { + return elaborate_method_func_(scope, prop, + use_darray->element_type(), + method_name, + "$ivl_queue_method$pop_back"); + } + if (method_name == "size") { + return elaborate_method_func_(scope, prop, + &netvector_t::atom2s32, + method_name, "$size"); + } + if (method_name == "reverse") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$reverse", + parm_names); + } + if (method_name == "sort") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$sort", + parm_names); + } + if (method_name == "rsort") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$rsort", + parm_names); + } + if (method_name == "shuffle") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$shuffle", + parm_names); + } + } else if (ptype && ptype->base_type() == IVL_VT_DARRAY) { + NetEProperty*prop = new NetEProperty(net, pidx, 0); + prop->set_line(*this); + if (method_name == "delete") { + static const std::vector parm_names = { + perm_string::literal("index") + }; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$delete", + parm_names); + } + if (method_name == "size") { + return elaborate_method_func_(scope, prop, + &netvector_t::atom2s32, + method_name, "$size"); + } + if (method_name == "reverse") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$reverse", + parm_names); + } + if (method_name == "sort") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$sort", + parm_names); + } + if (method_name == "rsort") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$rsort", + parm_names); + } + if (method_name == "shuffle") { + static const std::vector parm_names; + return elaborate_sys_task_method_(des, scope, prop, method_name, + "$ivl_darray_method$shuffle", + parm_names); + } + } + } + } + } + // Is this a method of a "string" type? if (dynamic_cast(net->net_type())) { if (method_name == "itoa") { static const std::vector parm_names = { perm_string::literal("i") }; - - return elaborate_sys_task_method_(des, scope, net, method_name, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, "$ivl_string_method$itoa", parm_names); } else if (method_name == "hextoa") { static const std::vector parm_names = { perm_string::literal("i") }; - - return elaborate_sys_task_method_(des, scope, net, method_name, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, "$ivl_string_method$hextoa", parm_names); } else if (method_name == "octtoa") { static const std::vector parm_names = { perm_string::literal("i") }; - - return elaborate_sys_task_method_(des, scope, net, method_name, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, "$ivl_string_method$octtoa", parm_names); } else if (method_name == "bintoa") { static const std::vector parm_names = { perm_string::literal("i") }; - - return elaborate_sys_task_method_(des, scope, net, method_name, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, "$ivl_string_method$bintoa", parm_names); } else if (method_name == "realtoa") { static const std::vector parm_names = { perm_string::literal("r") }; - - return elaborate_sys_task_method_(des, scope, net, method_name, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, "$ivl_string_method$realtoa", parm_names); } @@ -3937,50 +4090,59 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, static const std::vector parm_names = { perm_string::literal("index") }; - - return elaborate_sys_task_method_(des, scope, net, method_name, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, "$ivl_darray_method$delete", parm_names); } else if (method_name == "size") { // This returns an int. It could be removed, but keep for now. - return elaborate_method_func_(scope, net, + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_method_func_(scope, sig, &netvector_t::atom2s32, method_name, "$size"); } else if (method_name == "reverse") { - cerr << get_fileline() << ": sorry: 'reverse()' " - "array sorting method is not currently supported." - << endl; - des->errors += 1; - return 0; - } else if (method_name=="sort") { - cerr << get_fileline() << ": sorry: 'sort()' " - "array sorting method is not currently supported." - << endl; - des->errors += 1; - return 0; - } else if (method_name=="rsort") { - cerr << get_fileline() << ": sorry: 'rsort()' " - "array sorting method is not currently supported." - << endl; - des->errors += 1; - return 0; - } else if (method_name=="shuffle") { - cerr << get_fileline() << ": sorry: 'shuffle()' " - "array sorting method is not currently supported." - << endl; - des->errors += 1; - return 0; + static const std::vector parm_names; + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, + "$ivl_darray_method$reverse", + parm_names); + } else if (method_name == "sort") { + static const std::vector parm_names; + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, + "$ivl_darray_method$sort", + parm_names); + } else if (method_name == "rsort") { + static const std::vector parm_names; + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, + "$ivl_darray_method$rsort", + parm_names); + } else if (method_name == "shuffle") { + static const std::vector parm_names; + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + return elaborate_sys_task_method_(des, scope, sig, method_name, + "$ivl_darray_method$shuffle", + parm_names); } } if (net->queue_type()) { const netdarray_t*use_darray = net->darray_type(); + NetESignal*qs = new NetESignal(net); + qs->set_line(*this); if (method_name == "push_back") { static const std::vector parm_names = { perm_string::literal("item") }; - return elaborate_queue_method_(des, scope, net, method_name, + return elaborate_queue_method_(des, scope, qs, method_name, "$ivl_queue_method$push_back", parm_names); } else if (method_name == "push_front") { @@ -3988,7 +4150,7 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, perm_string::literal("item") }; - return elaborate_queue_method_(des, scope, net, method_name, + return elaborate_queue_method_(des, scope, qs, method_name, "$ivl_queue_method$push_front", parm_names); } else if (method_name == "insert") { @@ -3997,16 +4159,16 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, perm_string::literal("item") }; - return elaborate_queue_method_(des, scope, net, method_name, + return elaborate_queue_method_(des, scope, qs, method_name, "$ivl_queue_method$insert", parm_names); } else if (method_name == "pop_front") { - return elaborate_method_func_(scope, net, + return elaborate_method_func_(scope, qs, use_darray->element_type(), method_name, "$ivl_queue_method$pop_front"); } else if (method_name == "pop_back") { - return elaborate_method_func_(scope, net, + return elaborate_method_func_(scope, qs, use_darray->element_type(), method_name, "$ivl_queue_method$pop_back"); diff --git a/eval_tree.cc b/eval_tree.cc index 7cbb49321..6c465daf1 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -26,6 +26,7 @@ # include # include "netlist.h" +# include "netclass.h" # include "ivl_assert.h" # include "netmisc.h" @@ -2182,6 +2183,27 @@ static bool get_array_info(const NetExpr*arg, long dim, left = range.get_msb(); right = range.get_lsb(); return false; + } + /* Class property (e.g. queue field): size is dynamic; defer to runtime + * instead of folding to all-X in evaluate_array_funcs_. */ + if (const NetEProperty*prop = dynamic_cast(arg)) { + const NetNet*obj = prop->get_sig(); + const netclass_t*cls = dynamic_cast(obj->net_type()); + if (cls == 0) + return true; + ivl_type_t ptype = cls->get_prop_type(prop->property_idx()); + if (ptype == 0) + return true; + switch (ptype->base_type()) { + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + case IVL_VT_STRING: + defer = true; + return true; + default: + break; + } + return true; } /* The argument must be a signal that has enough dimensions. */ const NetESignal*esig = dynamic_cast(arg); diff --git a/ivl_target.h b/ivl_target.h index b33ea9aa4..b4eec2c3b 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -2400,6 +2400,10 @@ extern int ivl_type_properties(ivl_type_t net); extern const char* ivl_type_prop_name(ivl_type_t net, int idx); extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx); +/* Maximum element count for a queue type (0 = unbounded). Only valid + * when ivl_type_base(net) == IVL_VT_QUEUE. */ +extern unsigned ivl_type_queue_max(ivl_type_t net); + #if defined(__MINGW32__) || defined (__CYGWIN__) # define DLLEXPORT __declspec(dllexport) diff --git a/ivtest/gold/always_comb_warn.gold b/ivtest/gold/always_comb_warn.gold index d9f2df207..183d31c11 100644 --- a/ivtest/gold/always_comb_warn.gold +++ b/ivtest/gold/always_comb_warn.gold @@ -23,7 +23,7 @@ ./ivltests/always_comb_warn.v:32: warning: A for statement step must be a simple binary +/- to be synthesized in an always_comb process. ./ivltests/always_comb_warn.v:32: warning: System task ($display) cannot be synthesized in an always_comb process. ./ivltests/always_comb_warn.v:33: warning: System task ($display) cannot be synthesized in an always_comb process. -./ivltests/always_comb_warn.v:34: warning: Dynamic array delete method cannot be synthesized in an always_comb process. +./ivltests/always_comb_warn.v:34: warning: Dynamic array ordering/delete method cannot be synthesized in an always_comb process. ./ivltests/always_comb_warn.v:35: warning: System task ($display) cannot be synthesized in an always_comb process. ./ivltests/always_comb_warn.v:14: warning: An event (tevt) cannot be synthesized in an always_comb process. ./ivltests/always_comb_warn.v:13: warning: A non-integral variable (trl) cannot be synthesized in an always_comb process. diff --git a/ivtest/gold/always_ff_warn.gold b/ivtest/gold/always_ff_warn.gold index 8f13486a6..335e4a82f 100644 --- a/ivtest/gold/always_ff_warn.gold +++ b/ivtest/gold/always_ff_warn.gold @@ -21,7 +21,7 @@ ./ivltests/always_ff_warn.v:33: warning: A for statement step must be a simple binary +/- to be synthesized in an always_ff process. ./ivltests/always_ff_warn.v:33: warning: System task ($display) cannot be synthesized in an always_ff process. ./ivltests/always_ff_warn.v:34: warning: System task ($display) cannot be synthesized in an always_ff process. -./ivltests/always_ff_warn.v:35: warning: Dynamic array delete method cannot be synthesized in an always_ff process. +./ivltests/always_ff_warn.v:35: warning: Dynamic array ordering/delete method cannot be synthesized in an always_ff process. ./ivltests/always_ff_warn.v:36: warning: System task ($display) cannot be synthesized in an always_ff process. ./ivltests/always_ff_warn.v:15: warning: An event (tevt) cannot be synthesized in an always_ff process. ./ivltests/always_ff_warn.v:14: warning: A non-integral variable (trl) cannot be synthesized in an always_ff process. diff --git a/ivtest/gold/always_latch_warn.gold b/ivtest/gold/always_latch_warn.gold index deab07d67..2cef5b242 100644 --- a/ivtest/gold/always_latch_warn.gold +++ b/ivtest/gold/always_latch_warn.gold @@ -21,7 +21,7 @@ ./ivltests/always_latch_warn.v:32: warning: A for statement step must be a simple binary +/- to be synthesized in an always_latch process. ./ivltests/always_latch_warn.v:32: warning: System task ($display) cannot be synthesized in an always_latch process. ./ivltests/always_latch_warn.v:33: warning: System task ($display) cannot be synthesized in an always_latch process. -./ivltests/always_latch_warn.v:34: warning: Dynamic array delete method cannot be synthesized in an always_latch process. +./ivltests/always_latch_warn.v:34: warning: Dynamic array ordering/delete method cannot be synthesized in an always_latch process. ./ivltests/always_latch_warn.v:35: warning: System task ($display) cannot be synthesized in an always_latch process. ./ivltests/always_latch_warn.v:14: warning: An event (tevt) cannot be synthesized in an always_latch process. ./ivltests/always_latch_warn.v:13: warning: A non-integral variable (trl) cannot be synthesized in an always_latch process. diff --git a/ivtest/gold/br1005.gold b/ivtest/gold/br1005.gold index e59c1def5..c054f026b 100644 --- a/ivtest/gold/br1005.gold +++ b/ivtest/gold/br1005.gold @@ -1,18 +1,5 @@ -./ivltests/br1005.v:2: sorry: Queues inside classes are not yet supported. -./ivltests/br1005.v:15: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:16: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:17: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:18: error: Enable of unknown task ``a.q.push_back''. -./ivltests/br1005.v:19: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:19: : method path: q.pop_front -./ivltests/br1005.v:19: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:22: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:22: : method path: q.pop_front -./ivltests/br1005.v:22: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:25: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:25: : method path: q.pop_front -./ivltests/br1005.v:25: error: Object test.a has no method "q.pop_front(...)". -./ivltests/br1005.v:28: sorry: Method name nesting is not supported yet. -./ivltests/br1005.v:28: : method path: q.pop_front -./ivltests/br1005.v:28: error: Object test.a has no method "q.pop_front(...)". -9 error(s) during elaboration. +1 +2 +3 +4 +PASSED diff --git a/ivtest/ivltests/README_sv_queue_locators.txt b/ivtest/ivltests/README_sv_queue_locators.txt new file mode 100644 index 000000000..3670bd62a --- /dev/null +++ b/ivtest/ivltests/README_sv_queue_locators.txt @@ -0,0 +1,88 @@ +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, min, max, unique, unique_index, + sum/product (integral), reverse (ordering), sort/rsort/shuffle (ordering) + +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). + + * min() and max() return queues containing all elements equal to the + selected extrema. + + * sum() returns the scalar reduction sum of elements (integral vector types); + empty arrays/queues yield 0. `sum() with (expr)` reduces the expression + result for each item. + + * product() returns the scalar reduction product of elements (integral + vector types). + `product() with (expr)` reduces the expression result for each item. + + * reverse() reverses the order of elements in place for queues and dynamic + arrays (integral, vector, real, string, and class-handle elements as stored + by Icarus). + + * sort() and rsort() order elements in ascending or descending order. Packed + vector elements use the same unsigned relational rules as min()/max(). + Class-handle arrays use a stable implementation-defined order + (sort_order_key on vvp_object_t). + + * shuffle() permutes elements randomly (std::mt19937); order is not portable + across runs. + + * 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*; + tgt-vvp/vvp_process.c — ordering/delete as system tasks (`%sort/obj`, …). + * VVP: vvp/vthread.cc — opcode implementations; vvp/vvp_darray.{h,cc} — + sort_elems(), shuffle_elems(), reverse_elems(); 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. + sv_darray_unique.v unique() and unique_index() on int[] dynamic array. + sv_queue_min_max.v min() and max() on queue values. + sv_darray_min_max.v min() and max() on dynamic array values. + sv_queue_min_max_with.v min()/max() with predicate on queue values. + sv_darray_min_max_with.v min()/max() with predicate on dynamic arrays. + sv_class_darray_prop_locators.v locator methods on class dynamic-array properties + (including sum/product with expression). + sv_queue_unique_with.v unique()/unique_index() with predicate on queues. + sv_darray_unique_with.v unique()/unique_index() with predicate on dynamic arrays. + sv_class_queue_prop_locators.v locator methods on class queue properties + (including sum/product with expression). + sv_queue_product.v integral product() reduction on queues. + sv_darray_product.v integral product() reduction on dynamic arrays. + sv_queue_product_with.v product() with expression on queues. + sv_darray_product_with.v product() with expression on dynamic arrays. + sv_darray_reverse.v reverse() ordering on dynamic arrays. + sv_darray_sort.v sort(), rsort(), shuffle() on dynamic arrays. + sv_queue_reverse.v reverse() ordering on queues. + sv_queue_sort.v sort(), rsort(), shuffle() on queues. + sv_queue_sum.v integral sum() reduction on queues. + sv_darray_sum.v integral sum() reduction on dynamic arrays. + sv_queue_sum_with.v sum() with expression on queues. + sv_darray_sum_with.v sum() with expression on dynamic arrays. diff --git a/ivtest/ivltests/sv_call_chain_method1.v b/ivtest/ivltests/sv_call_chain_method1.v new file mode 100644 index 000000000..d6ba43dee --- /dev/null +++ b/ivtest/ivltests/sv_call_chain_method1.v @@ -0,0 +1,25 @@ +// Chained call: function returns class handle, then method on result (a().b()). + +module test; + + class C; + function int f; + f = 7; + endfunction + endclass + + function C get_c; + get_c = new; + endfunction + + initial begin + int x; + x = get_c().f(); + if (x !== 7) begin + $display("FAILED"); + end else begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_class_darray_prop_locators.v b/ivtest/ivltests/sv_class_darray_prop_locators.v new file mode 100644 index 000000000..58f584f45 --- /dev/null +++ b/ivtest/ivltests/sv_class_darray_prop_locators.v @@ -0,0 +1,80 @@ +// Regression: class dynamic-array property locator methods. + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do if ((val) !== (exp)) begin $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); failed = 1'b1; end while(0) + + class C; + int d[]; + endclass + + C c; + int r[$]; + + initial begin + c = new; + c.d = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + + r = c.d.find() with (item > 3); + `check(r.size, 5); + `check(r[0], 4); + `check(r[4], 6); + + r = c.d.find_first() with (item > 6); + `check(r.size, 1); + `check(r[0], 7); + + r = c.d.find_last_index() with (item < 3); + `check(r.size, 1); + `check(r[0], 8); + + r = c.d.unique(); + `check(r.size, 7); + `check(r[0], 4); + + r = c.d.unique_index(); + `check(r.size, 7); + `check(r[0], 0); + + r = c.d.unique() with (item > 2); + `check(r.size, 5); + `check(r[0], 4); + `check(r[4], 3); + + r = c.d.unique_index() with (item > 2); + `check(r.size, 6); + `check(r[0], 0); + `check(r[5], 7); + + r = c.d.min(); + `check(r.size, 2); + `check(r[0], 1); + `check(r[1], 1); + + r = c.d.max() with (item < 7); + `check(r.size, 1); + `check(r[0], 6); + + c.d = '{2, 3, 4}; + `check(c.d.product(), 24); + `check(c.d.sum() with (item + 1), 12); + `check(c.d.product() with (item + 1), 60); + + c.d = '{1, 2, 3}; + c.d.reverse(); + `check(c.d[0], 3); + `check(c.d[1], 2); + `check(c.d[2], 1); + + c.d = '{3, 1, 2}; + c.d.sort(); + `check(c.d[0], 1); + `check(c.d[1], 2); + `check(c.d[2], 3); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_class_queue_prop_locators.v b/ivtest/ivltests/sv_class_queue_prop_locators.v new file mode 100644 index 000000000..eabb557d4 --- /dev/null +++ b/ivtest/ivltests/sv_class_queue_prop_locators.v @@ -0,0 +1,72 @@ +// Regression: class queue property locator methods. + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do if ((val) !== (exp)) begin $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); failed = 1'b1; end while (0) + + class C; + int q[$]; + endclass + + C c; + int r[$]; + + initial begin + c = new; + c.q = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + + r = c.q.find() with (item > 3); + `check(r.size, 5); + `check(r[0], 4); + `check(r[4], 6); + + r = c.q.find_last_index() with (item < 3); + `check(r.size, 1); + `check(r[0], 8); + + r = c.q.unique() with (item > 2); + `check(r.size, 5); + `check(r[0], 4); + `check(r[4], 3); + + r = c.q.unique_index() with (item > 2); + `check(r.size, 6); + `check(r[0], 0); + `check(r[5], 7); + + r = c.q.min(); + `check(r.size, 2); + `check(r[0], 1); + `check(r[1], 1); + + r = c.q.max() with (item < 7); + `check(r.size, 1); + `check(r[0], 6); + + r = c.q.max(); + `check(r.size, 2); + `check(r[0], 7); + + c.q = '{2, 3, 4}; + `check(c.q.product(), 24); + `check(c.q.sum() with (item + 1), 12); + `check(c.q.product() with (item + 1), 60); + + c.q = '{1, 2, 3}; + c.q.reverse(); + `check(c.q[0], 3); + `check(c.q[1], 2); + `check(c.q[2], 1); + + c.q = '{3, 1, 2}; + c.q.sort(); + `check(c.q[0], 1); + `check(c.q[1], 2); + `check(c.q[2], 3); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_class_queue_prop_methods.v b/ivtest/ivltests/sv_class_queue_prop_methods.v new file mode 100644 index 000000000..47d879bd7 --- /dev/null +++ b/ivtest/ivltests/sv_class_queue_prop_methods.v @@ -0,0 +1,39 @@ +// Regression: queue-typed class properties — push_front/push_back and +// pop_front/pop_back. (VVP asm must recognize %store/prop/qf/* and +// %qpop/prop/*; opcode_table must stay lexicographically sorted.) + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). expected %0d, got %0d", `__LINE__, exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int q[$]; + endclass + + C c; + int t; + + initial begin + c = new; + c.q.push_back(1); + c.q.push_front(0); + c.q.push_back(2); + t = c.q.pop_back(); + `check(t, 32'd2); + `check(c.q.size(), 32'd2); + t = c.q.pop_front(); + `check(t, 32'd0); + `check(c.q[0], 32'd1); + + if (!failed) begin + $display("PASSED"); + end + end +endmodule 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_darray_min_max.v b/ivtest/ivltests/sv_darray_min_max.v new file mode 100644 index 000000000..58265c560 --- /dev/null +++ b/ivtest/ivltests/sv_darray_min_max.v @@ -0,0 +1,36 @@ +// Regression: dynamic array min() and max() locator methods return queues. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[] = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + int empty[]; + int r[$]; + + initial begin + r = a.min(); + `CHK(r.size == 2); + `CHK(r[0] == 1); + `CHK(r[1] == 1); + + r = a.max(); + `CHK(r.size == 2); + `CHK(r[0] == 7); + `CHK(r[1] == 7); + + r = empty.min(); + `CHK(r.size == 0); + r = empty.max(); + `CHK(r.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_min_max_with.v b/ivtest/ivltests/sv_darray_min_max_with.v new file mode 100644 index 000000000..834822932 --- /dev/null +++ b/ivtest/ivltests/sv_darray_min_max_with.v @@ -0,0 +1,30 @@ +// Regression: dynamic array min/max with(predicate) locator methods. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int a[] = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + int r[$]; + + initial begin + r = a.min() with (item > 3); + `CHK(r.size == 1); + `CHK(r[0] == 4); + + r = a.max() with (item < 7); + `CHK(r.size == 1); + `CHK(r[0] == 6); + + r = a.min() with (item > 99); + `CHK(r.size == 0); + + r = a.max() with (item > 99); + `CHK(r.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_product.v b/ivtest/ivltests/sv_darray_product.v new file mode 100644 index 000000000..e9a35181c --- /dev/null +++ b/ivtest/ivltests/sv_darray_product.v @@ -0,0 +1,24 @@ +// Regression: dynamic array product() reduction (integral). + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int a[]; + int p; + + initial begin + a = '{2, 3, 4}; + p = a.product(); + `CHK(p === 24); + + a = '{-2, 3, 5}; + p = a.product(); + `CHK(p === -30); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_product_with.v b/ivtest/ivltests/sv_darray_product_with.v new file mode 100644 index 000000000..11a706468 --- /dev/null +++ b/ivtest/ivltests/sv_darray_product_with.v @@ -0,0 +1,24 @@ +// Regression: dynamic array product() reduction with expression. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int a[]; + int p; + + initial begin + a = '{10, -3, 5}; + + p = a.product() with (item > 0); + `CHK(p === 0); + + p = a.product() with (item + 1); + `CHK(p === -132); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_reverse.v b/ivtest/ivltests/sv_darray_reverse.v new file mode 100644 index 000000000..b55ddf6c9 --- /dev/null +++ b/ivtest/ivltests/sv_darray_reverse.v @@ -0,0 +1,32 @@ +// Regression: dynamic array reverse() ordering method. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[]; + + initial begin + a = '{1, 2, 3, 4}; + a.reverse(); + `CHK(a[0] === 4 && a[1] === 3 && a[2] === 2 && a[3] === 1); + + a = new [1]; + a[0] = 42; + a.reverse(); + `CHK(a[0] === 42); + + a = new [0]; + a.reverse(); + `CHK(a.size() === 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_sort.v b/ivtest/ivltests/sv_darray_sort.v new file mode 100644 index 000000000..14a04a21c --- /dev/null +++ b/ivtest/ivltests/sv_darray_sort.v @@ -0,0 +1,33 @@ +// Regression: dynamic array sort(), rsort(), shuffle(). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[]; + int sum0; + + initial begin + a = '{3, 1, 4, 1, 5}; + a.sort(); + `CHK(a[0] === 1 && a[1] === 1 && a[2] === 3 && a[3] === 4 && a[4] === 5); + + a = '{3, 1, 4}; + a.rsort(); + `CHK(a[0] === 4 && a[1] === 3 && a[2] === 1); + + a = '{10, -2, 7}; + sum0 = a.sum(); + a.shuffle(); + `CHK(a.sum() === sum0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_sum.v b/ivtest/ivltests/sv_darray_sum.v new file mode 100644 index 000000000..0ae6986fe --- /dev/null +++ b/ivtest/ivltests/sv_darray_sum.v @@ -0,0 +1,28 @@ +// Regression: dynamic array sum() reduction (integral). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int a[]; + int s; + + initial begin + a = new[0]; + s = a.sum(); + `CHK(s === 0); + + a = '{10, -3, 5}; + s = a.sum(); + `CHK(s === 12); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_sum_with.v b/ivtest/ivltests/sv_darray_sum_with.v new file mode 100644 index 000000000..4616821e1 --- /dev/null +++ b/ivtest/ivltests/sv_darray_sum_with.v @@ -0,0 +1,24 @@ +// Regression: dynamic array sum() reduction with expression. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int a[]; + int s; + + initial begin + a = '{10, -3, 5}; + + s = a.sum() with (item > 0); + `CHK(s === 2); + + s = a.sum() with (item + 1); + `CHK(s === 15); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_darray_unique.v b/ivtest/ivltests/sv_darray_unique.v new file mode 100644 index 000000000..ee0b00ef7 --- /dev/null +++ b/ivtest/ivltests/sv_darray_unique.v @@ -0,0 +1,29 @@ +// Regression: dynamic array unique() and unique_index(). + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int a[] = '{1, 2, 1, 3, 2}; + int u[$]; + int ix[$]; + + initial begin + u = a.unique(); + `CHK(u.size == 3); + `CHK(u[0] == 1); + `CHK(u[1] == 2); + `CHK(u[2] == 3); + + ix = a.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/ivltests/sv_darray_unique_with.v b/ivtest/ivltests/sv_darray_unique_with.v new file mode 100644 index 000000000..b4fa97518 --- /dev/null +++ b/ivtest/ivltests/sv_darray_unique_with.v @@ -0,0 +1,33 @@ +// Regression: dynamic array unique()/unique_index() with predicate. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int a[] = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + int r[$]; + + initial begin + r = a.unique() with (item > 2); + `CHK(r.size == 5); + `CHK(r[0] == 4); + `CHK(r[1] == 7); + `CHK(r[2] == 5); + `CHK(r[3] == 6); + `CHK(r[4] == 3); + + r = a.unique_index() with (item > 2); + `CHK(r.size == 6); + `CHK(r[0] == 0); + `CHK(r[1] == 1); + `CHK(r[2] == 3); + `CHK(r[3] == 4); + `CHK(r[4] == 6); + `CHK(r[5] == 7); + + 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_min_max.v b/ivtest/ivltests/sv_queue_min_max.v new file mode 100644 index 000000000..872bc0d44 --- /dev/null +++ b/ivtest/ivltests/sv_queue_min_max.v @@ -0,0 +1,47 @@ +// Regression: queue min() and max() locator methods return queues. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int e[$]; + int r[$]; + + 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); + + r = q.min(); + `CHK(r.size == 2); + `CHK(r[0] == 1); + `CHK(r[1] == 1); + + r = q.max(); + `CHK(r.size == 2); + `CHK(r[0] == 7); + `CHK(r[1] == 7); + + r = e.min(); + `CHK(r.size == 0); + r = e.max(); + `CHK(r.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_min_max_with.v b/ivtest/ivltests/sv_queue_min_max_with.v new file mode 100644 index 000000000..86607af8b --- /dev/null +++ b/ivtest/ivltests/sv_queue_min_max_with.v @@ -0,0 +1,41 @@ +// Regression: queue min/max with(predicate) locator methods. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int q[$]; + int r[$]; + + 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); + + r = q.min() with (item > 3); + `CHK(r.size == 1); + `CHK(r[0] == 4); + + r = q.max() with (item < 7); + `CHK(r.size == 1); + `CHK(r[0] == 6); + + r = q.min() with (item > 99); + `CHK(r.size == 0); + + r = q.max() with (item > 99); + `CHK(r.size == 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_product.v b/ivtest/ivltests/sv_queue_product.v new file mode 100644 index 000000000..28dbe3f27 --- /dev/null +++ b/ivtest/ivltests/sv_queue_product.v @@ -0,0 +1,24 @@ +// Regression: queue product() reduction (integral). + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int q[$]; + int p; + + initial begin + q = '{2, 3, 4}; + p = q.product(); + `CHK(p === 24); + + q = '{-2, 3, 5}; + p = q.product(); + `CHK(p === -30); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_product_with.v b/ivtest/ivltests/sv_queue_product_with.v new file mode 100644 index 000000000..62af05150 --- /dev/null +++ b/ivtest/ivltests/sv_queue_product_with.v @@ -0,0 +1,24 @@ +// Regression: queue product() reduction with expression. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int q[$]; + int p; + + initial begin + q = '{4, 7, 2}; + + p = q.product() with (item > 3); + `CHK(p === 0); + + p = q.product() with (item + 1); + `CHK(p === 120); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_reverse.v b/ivtest/ivltests/sv_queue_reverse.v new file mode 100644 index 000000000..5e7574cfb --- /dev/null +++ b/ivtest/ivltests/sv_queue_reverse.v @@ -0,0 +1,31 @@ +// Regression: queue reverse() ordering method. + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + + initial begin + q = '{1, 2, 3, 4}; + q.reverse(); + `CHK(q[0] === 4 && q[1] === 3 && q[2] === 2 && q[3] === 1); + + q = '{99}; + q.reverse(); + `CHK(q[0] === 99); + + q.delete(); + q.reverse(); + `CHK(q.size() === 0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_sort.v b/ivtest/ivltests/sv_queue_sort.v new file mode 100644 index 000000000..f48b2e4dd --- /dev/null +++ b/ivtest/ivltests/sv_queue_sort.v @@ -0,0 +1,33 @@ +// Regression: queue sort(), rsort(), shuffle(). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int sum0; + + initial begin + q = '{3, 1, 4, 1, 5}; + q.sort(); + `CHK(q[0] === 1 && q[1] === 1 && q[2] === 3 && q[3] === 4 && q[4] === 5); + + q = '{3, 1, 4}; + q.rsort(); + `CHK(q[0] === 4 && q[1] === 3 && q[2] === 1); + + q = '{10, -2, 7}; + sum0 = q.sum(); + q.shuffle(); + `CHK(q.sum() === sum0); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_sum.v b/ivtest/ivltests/sv_queue_sum.v new file mode 100644 index 000000000..49baa17ee --- /dev/null +++ b/ivtest/ivltests/sv_queue_sum.v @@ -0,0 +1,28 @@ +// Regression: queue sum() reduction (integral). + +module top; + + bit failed = 0; + + `define CHK(cond) \ + if (!(cond)) begin \ + $display("FAILED line %0d", `__LINE__); \ + failed = 1; \ + end + + int q[$]; + int s; + + initial begin + q = '{}; + s = q.sum(); + `CHK(s === 0); + + q = '{4, 7, 2}; + s = q.sum(); + `CHK(s === 13); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/ivltests/sv_queue_sum_with.v b/ivtest/ivltests/sv_queue_sum_with.v new file mode 100644 index 000000000..907173c8e --- /dev/null +++ b/ivtest/ivltests/sv_queue_sum_with.v @@ -0,0 +1,24 @@ +// Regression: queue sum() reduction with expression. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int q[$]; + int s; + + initial begin + q = '{4, 7, 2, 5}; + + s = q.sum() with (item > 3); + `CHK(s === 3); + + s = q.sum() with (item * 2); + `CHK(s === 36); + + 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/ivltests/sv_queue_unique_with.v b/ivtest/ivltests/sv_queue_unique_with.v new file mode 100644 index 000000000..1957a8365 --- /dev/null +++ b/ivtest/ivltests/sv_queue_unique_with.v @@ -0,0 +1,35 @@ +// Regression: queue unique()/unique_index() with predicate. + +module top; + + bit failed = 0; + + `define CHK(cond) if (!(cond)) begin $display("FAILED line %0d", `__LINE__); failed = 1; end + + int q[$]; + int r[$]; + + initial begin + q = '{4, 7, 2, 5, 7, 1, 6, 3, 1}; + + r = q.unique() with (item > 2); + `CHK(r.size == 5); + `CHK(r[0] == 4); + `CHK(r[1] == 7); + `CHK(r[2] == 5); + `CHK(r[3] == 6); + `CHK(r[4] == 3); + + r = q.unique_index() with (item > 2); + `CHK(r.size == 6); + `CHK(r[0] == 0); + `CHK(r[1] == 1); + `CHK(r[2] == 3); + `CHK(r[3] == 4); + `CHK(r[4] == 6); + `CHK(r[5] == 7); + + if (!failed) + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-ivl1.list b/ivtest/regress-ivl1.list index af0a5b3e5..599805f1c 100644 --- a/ivtest/regress-ivl1.list +++ b/ivtest/regress-ivl1.list @@ -274,7 +274,6 @@ array_lval_select3a CE ivltests br605a EF ivltests br605b EF ivltests br971 EF ivltests -br1005 CE,-g2009 ivltests br1015b CE,-g2009 ivltests br_ml20150315b CE,-g2009 ivltests sv_deferred_assert1 CE,-g2009 ivltests gold=sv_deferred_assert1.gold diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index 3f82374c8..a3ac53a64 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -360,6 +360,7 @@ unp_array_typedef CE,-g2009,-pallowsigned=1 ivltests # Also string br959 CE,-g2009 ivltests br1003a CE,-g2009 ivltests br1004 CE,-g2009 ivltests +br1005 CE,-g2009 ivltests br_gh104a CE,-g2009 ivltests br_gh167a CE,-g2009 ivltests br_gh167b CE,-g2009 ivltests diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 2cde03b79..36dad8e10 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -224,6 +224,7 @@ sv_array_cassign6 vvp_tests/sv_array_cassign6.json sv_array_cassign7 vvp_tests/sv_array_cassign7.json sv_array_cassign8 vvp_tests/sv_array_cassign8.json sv_automatic_2state vvp_tests/sv_automatic_2state.json +sv_call_chain_method1 vvp_tests/sv_call_chain_method1.json sv_chained_constructor1 vvp_tests/sv_chained_constructor1.json sv_chained_constructor2 vvp_tests/sv_chained_constructor2.json sv_chained_constructor3 vvp_tests/sv_chained_constructor3.json @@ -232,11 +233,14 @@ sv_chained_constructor5 vvp_tests/sv_chained_constructor5.json sv_class_prop_assign_op1 vvp_tests/sv_class_prop_assign_op1.json sv_class_prop_assign_op2 vvp_tests/sv_class_prop_assign_op2.json sv_class_prop_logic vvp_tests/sv_class_prop_logic.json +sv_class_darray_prop_locators vvp_tests/sv_class_darray_prop_locators.json +sv_class_queue_prop_locators vvp_tests/sv_class_queue_prop_locators.json sv_class_prop_nest_darray1 vvp_tests/sv_class_prop_nest_darray1.json sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_str1.json sv_class_prop_nest_str1 vvp_tests/sv_class_prop_nest_real1.json sv_class_prop_nest_vec1 vvp_tests/sv_class_prop_nest_vec1.json +sv_class_queue_prop_methods vvp_tests/sv_class_queue_prop_methods.json sv_const1 vvp_tests/sv_const1.json sv_const2 vvp_tests/sv_const2.json sv_const3 vvp_tests/sv_const3.json @@ -252,6 +256,17 @@ 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_darray_min_max vvp_tests/sv_darray_min_max.json +sv_darray_min_max_with vvp_tests/sv_darray_min_max_with.json +sv_darray_product vvp_tests/sv_darray_product.json +sv_darray_product_with vvp_tests/sv_darray_product_with.json +sv_darray_reverse vvp_tests/sv_darray_reverse.json +sv_darray_sort vvp_tests/sv_darray_sort.json +sv_darray_sum vvp_tests/sv_darray_sum.json +sv_darray_sum_with vvp_tests/sv_darray_sum_with.json +sv_darray_unique vvp_tests/sv_darray_unique.json +sv_darray_unique_with vvp_tests/sv_darray_unique_with.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 +286,18 @@ 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_queue_min_max vvp_tests/sv_queue_min_max.json +sv_queue_min_max_with vvp_tests/sv_queue_min_max_with.json +sv_queue_product vvp_tests/sv_queue_product.json +sv_queue_product_with vvp_tests/sv_queue_product_with.json +sv_queue_reverse vvp_tests/sv_queue_reverse.json +sv_queue_sort vvp_tests/sv_queue_sort.json +sv_queue_sum vvp_tests/sv_queue_sum.json +sv_queue_sum_with vvp_tests/sv_queue_sum_with.json sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json sdf_header vvp_tests/sdf_header.json task_return1 vvp_tests/task_return1.json diff --git a/ivtest/vvp_tests/sv_call_chain_method1.json b/ivtest/vvp_tests/sv_call_chain_method1.json new file mode 100644 index 000000000..edd7b87fd --- /dev/null +++ b/ivtest/vvp_tests/sv_call_chain_method1.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_call_chain_method1.v", + "iverilog-args" : [ "-g2012" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_class_darray_prop_locators.json b/ivtest/vvp_tests/sv_class_darray_prop_locators.json new file mode 100644 index 000000000..2cd98f13c --- /dev/null +++ b/ivtest/vvp_tests/sv_class_darray_prop_locators.json @@ -0,0 +1,10 @@ +{ + "type" : "normal", + "source" : "sv_class_darray_prop_locators.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes/dynamic arrays are not supported", + "type" : "CE", + "iverilog-args" : [ "-pallowsigned=1" ] + } +} diff --git a/ivtest/vvp_tests/sv_class_queue_prop_locators.json b/ivtest/vvp_tests/sv_class_queue_prop_locators.json new file mode 100644 index 000000000..cde66114a --- /dev/null +++ b/ivtest/vvp_tests/sv_class_queue_prop_locators.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_queue_prop_locators.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog class queue property locator methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_class_queue_prop_methods.json b/ivtest/vvp_tests/sv_class_queue_prop_methods.json new file mode 100644 index 000000000..735de2f79 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_queue_prop_methods.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_queue_prop_methods.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} 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_darray_min_max.json b/ivtest/vvp_tests/sv_darray_min_max.json new file mode 100644 index 000000000..d184b70b4 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_min_max.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_min_max.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array min/max locator methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_min_max_with.json b/ivtest/vvp_tests/sv_darray_min_max_with.json new file mode 100644 index 000000000..e8d3e6c94 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_min_max_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_min_max_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array min/max with predicate", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_product.json b/ivtest/vvp_tests/sv_darray_product.json new file mode 100644 index 000000000..75501c439 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_product.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_product.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array product() reduction", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_product_with.json b/ivtest/vvp_tests/sv_darray_product_with.json new file mode 100644 index 000000000..2b838ed3f --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_product_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_product_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array product() with expression", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_reverse.json b/ivtest/vvp_tests/sv_darray_reverse.json new file mode 100644 index 000000000..11b7c17d9 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_reverse.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_reverse.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array reverse()", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_sort.json b/ivtest/vvp_tests/sv_darray_sort.json new file mode 100644 index 000000000..dac245910 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_sort.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_sort.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array sort/rsort/shuffle", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_sum.json b/ivtest/vvp_tests/sv_darray_sum.json new file mode 100644 index 000000000..844957ccc --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_sum.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_sum.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array sum() reduction", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_sum_with.json b/ivtest/vvp_tests/sv_darray_sum_with.json new file mode 100644 index 000000000..5146f2344 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_sum_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_sum_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array sum() with expression", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_unique.json b/ivtest/vvp_tests/sv_darray_unique.json new file mode 100644 index 000000000..a5e5c5ad8 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_unique.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_unique.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog dynamic array unique methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_darray_unique_with.json b/ivtest/vvp_tests/sv_darray_unique_with.json new file mode 100644 index 000000000..251b10a43 --- /dev/null +++ b/ivtest/vvp_tests/sv_darray_unique_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_darray_unique_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog darray unique with predicate", + "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_min_max.json b/ivtest/vvp_tests/sv_queue_min_max.json new file mode 100644 index 000000000..623bad2f8 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_min_max.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_min_max.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue min/max locator methods", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_min_max_with.json b/ivtest/vvp_tests/sv_queue_min_max_with.json new file mode 100644 index 000000000..82b176dda --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_min_max_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_min_max_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue min/max with predicate", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_product.json b/ivtest/vvp_tests/sv_queue_product.json new file mode 100644 index 000000000..84bb5e979 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_product.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_product.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue product() reduction", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_product_with.json b/ivtest/vvp_tests/sv_queue_product_with.json new file mode 100644 index 000000000..2ce9df377 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_product_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_product_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue product() with expression", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_reverse.json b/ivtest/vvp_tests/sv_queue_reverse.json new file mode 100644 index 000000000..771b7dbea --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_reverse.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_reverse.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue reverse()", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_sort.json b/ivtest/vvp_tests/sv_queue_sort.json new file mode 100644 index 000000000..a37d4b395 --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_sort.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_sort.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue sort/rsort/shuffle", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_sum.json b/ivtest/vvp_tests/sv_queue_sum.json new file mode 100644 index 000000000..523bc9dee --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_sum.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_sum.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue sum() reduction", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_queue_sum_with.json b/ivtest/vvp_tests/sv_queue_sum_with.json new file mode 100644 index 000000000..6ab5de32a --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_sum_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_sum_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue sum() with expression", + "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/ivtest/vvp_tests/sv_queue_unique_with.json b/ivtest/vvp_tests/sv_queue_unique_with.json new file mode 100644 index 000000000..f353cb83c --- /dev/null +++ b/ivtest/vvp_tests/sv_queue_unique_with.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_queue_unique_with.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "SystemVerilog queue unique with predicate", + "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/netlist.cc b/netlist.cc index b26c508ec..906708bcf 100644 --- a/netlist.cc +++ b/netlist.cc @@ -3454,9 +3454,13 @@ bool NetScope::check_synth(ivl_process_type_t pr_type, bool NetSTask::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */) const { - if (strcmp(name(), "$ivl_darray_method$delete") == 0) { + if (strcmp(name(), "$ivl_darray_method$delete") == 0 || + strcmp(name(), "$ivl_darray_method$reverse") == 0 || + strcmp(name(), "$ivl_darray_method$sort") == 0 || + strcmp(name(), "$ivl_darray_method$rsort") == 0 || + strcmp(name(), "$ivl_darray_method$shuffle") == 0) { cerr << get_fileline() << ": warning: Dynamic array " - "delete method cannot be synthesized " + "ordering/delete method cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } else { cerr << get_fileline() << ": warning: System task (" 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 d1767a86b..4a9274666 100644 --- a/parse.y +++ b/parse.y @@ -756,7 +756,7 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, %type timeskew_fullskew_opt_remain_active_flag %type assignment_pattern expression expression_opt expr_mintypmax -%type expr_primary_or_typename expr_primary +%type expr_primary_or_typename expr_primary call_chain_expr %type class_new dynamic_array_new %type var_decl_initializer_opt initializer_opt %type inc_or_dec_expression inside_expression lpvalue @@ -3869,6 +3869,41 @@ expr_primary_or_typename ; + /* SystemVerilog: a().b() — call a function, then invoke a method on the + returned class handle. Extends with further ".id(args)" as needed. */ + +call_chain_expr + : hierarchy_identifier attribute_list_opt argument_list_parens + { PECallFunction*tmp = pform_make_call_function(@1, *$1, *$3); + delete $1; + delete $2; + delete $3; + $$ = tmp; + } + | call_chain_expr '.' hierarchy_identifier attribute_list_opt argument_list_parens + { PECallFunction*tmp = pform_make_chained_call_function(@2, $1, *$3, *$5); + delete $3; + delete $4; + 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 : number { assert($1); @@ -3928,6 +3963,20 @@ expr_primary /* The hierarchy_identifier rule matches simple identifiers as well as indexed arrays and part selects */ + /* SV call chains get_c1().f() — must come before bare hierarchy_identifier + so `id (` is not reduced as PEIdent + error. */ + | 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); @@ -3951,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"))); @@ -3967,7 +4008,6 @@ expr_primary $$ = tmp; delete nm; } - | package_scope hierarchy_identifier { lex_in_package_scope(0); $$ = pform_package_ident(@2, $1, $2); @@ -3978,13 +4018,6 @@ expr_primary function call. If a system identifier, then a system function call. It can also be a call to a class method (function). */ - | hierarchy_identifier attribute_list_opt argument_list_parens - { PECallFunction*tmp = pform_make_call_function(@1, *$1, *$3); - delete $1; - delete $2; - delete $3; - $$ = tmp; - } | class_hierarchy_identifier argument_list_parens { PECallFunction*tmp = pform_make_call_function(@1, *$1, *$2); delete $1; @@ -4474,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.cc b/pform.cc index c7e337b63..277312b05 100644 --- a/pform.cc +++ b/pform.cc @@ -938,6 +938,24 @@ PECallFunction* pform_make_call_function(const struct vlltype&loc, return tmp; } +PECallFunction* pform_make_chained_call_function(const struct vlltype&loc, + PExpr*prefix, + const pform_name_t&method, + const list &parms) +{ + if (!gn_system_verilog()) { + pform_requires_sv(loc, "Chained calls like a().b()"); + delete prefix; + return new PECallFunction(method, parms); + } + + check_potential_imports(loc, method.front().name, true); + + PECallFunction*tmp = new PECallFunction(prefix, method, parms); + FILE_NAME(tmp, loc); + return tmp; +} + PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const list &parms) diff --git a/pform.h b/pform.h index f7d5b491b..12d0e5704 100644 --- a/pform.h +++ b/pform.h @@ -324,6 +324,10 @@ extern void pform_set_type_referenced(const struct vlltype&loc, const char*name) extern PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, const std::list &parms); +extern PECallFunction* pform_make_chained_call_function(const struct vlltype&loc, + PExpr*prefix, + const pform_name_t&method, + const std::list &parms); extern PCallTask* pform_make_call_task(const struct vlltype&loc, const pform_name_t&name, const std::list &parms); diff --git a/pform_dump.cc b/pform_dump.cc index 6dc130687..313d94cf5 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -421,7 +421,13 @@ void PEConcat::dump(ostream&out) const void PECallFunction::dump(ostream &out) const { + if (peek_chain_prefix()) { + peek_chain_prefix()->dump(out); + out << "."; + } out << path_ << "(" << parms_ << ")"; + if (peek_with_clause()) + out << " with (" << *peek_with_clause() << ")"; } void PECastSize::dump(ostream &out) const diff --git a/t-dll-api.cc b/t-dll-api.cc index 7a33018f8..9aa69b4be 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -25,6 +25,7 @@ # include "discipline.h" # include "netclass.h" # include "netdarray.h" +# include "netqueue.h" # include "netenum.h" # include "netvector.h" # include @@ -3308,6 +3309,19 @@ extern "C" ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx) return class_type->get_prop_type(idx); } +extern "C" unsigned ivl_type_queue_max(ivl_type_t net) +{ + if (net == 0) + return 0; + if (const netqueue_t*que = dynamic_cast(net)) { + long max_idx = que->max_idx(); + if (max_idx < 0) + return 0; + return (unsigned)(max_idx + 1); + } + return 0; +} + extern "C" int ivl_type_signed(ivl_type_t net) { assert(net); diff --git a/tgt-vvp/draw_class.c b/tgt-vvp/draw_class.c index afbc26d5c..012dedbfc 100644 --- a/tgt-vvp/draw_class.c +++ b/tgt-vvp/draw_class.c @@ -63,6 +63,7 @@ static void show_prop_type(ivl_type_t ptype) show_prop_type_vector(ptype); break; case IVL_VT_DARRAY: + case IVL_VT_QUEUE: case IVL_VT_CLASS: fprintf(vvp_out, "\"o\""); if (packed_dimensions > 0) { diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index a49ffb308..16e56a018 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -375,6 +375,20 @@ static void draw_vpi_taskfunc_args(const char*call_string, } break; + case IVL_EX_PROPERTY: + if (ivl_expr_oper1(expr) == 0 && + (ivl_expr_value(expr) == IVL_VT_QUEUE || + ivl_expr_value(expr) == IVL_VT_DARRAY)) { + ivl_signal_t clas = ivl_expr_signal(expr); + unsigned pidx = ivl_expr_property_idx(expr); + unsigned isq = ivl_expr_value(expr) == IVL_VT_QUEUE ? 1U : 0U; + snprintf(buffer, sizeof buffer, "&PQ", + clas, pidx, isq); + args[idx].text = strdup(buffer); + continue; + } + break; + case IVL_EX_SIGNAL: case IVL_EX_SELECT: args[idx].stack = vec4_stack_need; @@ -402,6 +416,10 @@ static void draw_vpi_taskfunc_args(const char*call_string, switch (ivl_expr_value(expr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: + case IVL_VT_QUEUE: + case IVL_VT_DARRAY: + /* Queue/darray element selects may still carry the + * container ivl_expr_value; evaluate as vec4 like logic. */ draw_eval_vec4(expr); args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; args[idx].str_flag = 0; @@ -433,6 +451,20 @@ static void draw_vpi_taskfunc_args(const char*call_string, buffer[0] = 0; break; default: + /* See eval_vec4.c:draw_eval_vec4 — indexed selects sometimes + * carry an unexpected ivl_expr_value (e.g. NO_TYPE). */ + if (ivl_expr_type(expr) == IVL_EX_SELECT && + ivl_expr_oper2(expr) != 0) { + draw_eval_vec4(expr); + args[idx].vec_flag = ivl_expr_signed(expr)? 's' : 'u'; + args[idx].str_flag = 0; + args[idx].real_flag = 0; + args[idx].stack = vec4_stack_need; + args[idx].vec_wid = ivl_expr_width(expr); + vec4_stack_need += 1; + buffer[0] = 0; + break; + } fprintf(stderr, "%s:%u: Sorry, cannot generate code for argument %u.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), idx+1); fprintf(vvp_out, "\nXXXX Unexpected argument: call_string=<%s>, arg=%u, type=%d\n", diff --git a/tgt-vvp/eval_object.c b/tgt-vvp/eval_object.c index 055ef38ae..105750e25 100644 --- a/tgt-vvp/eval_object.c +++ b/tgt-vvp/eval_object.c @@ -173,6 +173,92 @@ static int eval_darray_new(ivl_expr_t ex) return errors; } +/* Build a queue/dynamic-array object value from an array pattern expression. */ +static int eval_array_pattern_object(ivl_expr_t ex) +{ + int errors = 0; + ivl_type_t net_type = ivl_expr_net_type(ex); + if (!net_type) + return 1; + ivl_variable_type_t base_type = ivl_type_base(net_type); + if (base_type != IVL_VT_DARRAY && base_type != IVL_VT_QUEUE) + return 1; + + ivl_type_t element_type = ivl_type_element(net_type); + if (!element_type) + return 1; + + unsigned max_elems = ivl_expr_parms(ex); + if (base_type == IVL_VT_QUEUE) { + unsigned max_size = ivl_type_queue_max(net_type); + if (max_size != 0 && max_elems > max_size) { + fprintf(stderr, "%s:%u: Warning: Array pattern assignment has more " + "elements (%u) than bounded queue supports (%u).\n" + " Only using first %u elements.\n", + ivl_expr_file(ex), ivl_expr_lineno(ex), max_elems, max_size, max_size); + max_elems = max_size; + } + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + } else { + unsigned size_reg = allocate_word(); + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", size_reg, max_elems); + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + darray_new(element_type, size_reg); + } + + switch (ivl_type_base(element_type)) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + for (unsigned idx = 0; idx < max_elems; idx += 1) { + draw_eval_vec4(ivl_expr_parm(ex, idx)); + if (base_type == IVL_VT_QUEUE) { + fprintf(vvp_out, " %%queue/append_word/v %u;\n", + ivl_type_packed_width(element_type)); + } else { + fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); + fprintf(vvp_out, " %%set/dar/obj/vec4 3;\n"); + fprintf(vvp_out, " %%pop/vec4 1;\n"); + } + } + break; + case IVL_VT_REAL: + for (unsigned idx = 0; idx < max_elems; idx += 1) { + if (base_type == IVL_VT_QUEUE) { + fprintf(vvp_out, "; ERROR: eval_array_pattern_object: queue real " + "element type not implemented\n"); + errors += 1; + break; + } + draw_eval_real(ivl_expr_parm(ex, idx)); + fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); + fprintf(vvp_out, " %%set/dar/obj/real 3;\n"); + fprintf(vvp_out, " %%pop/real 1;\n"); + } + break; + case IVL_VT_STRING: + for (unsigned idx = 0; idx < max_elems; idx += 1) { + if (base_type == IVL_VT_QUEUE) { + fprintf(vvp_out, "; ERROR: eval_array_pattern_object: queue string " + "element type not implemented\n"); + errors += 1; + break; + } + draw_eval_string(ivl_expr_parm(ex, idx)); + fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); + fprintf(vvp_out, " %%set/dar/obj/str 3;\n"); + fprintf(vvp_out, " %%pop/str 1;\n"); + } + break; + default: + fprintf(vvp_out, "; ERROR: eval_array_pattern_object: unsupported " + "element type %d\n", ivl_type_base(element_type)); + errors += 1; + break; + } + + return errors; +} + static int eval_class_new(ivl_expr_t ex) { ivl_type_t class_type = ivl_expr_net_type(ex); @@ -257,6 +343,517 @@ 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, 1;\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, 1;\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; + } + + if (strcmp(name, "$ivl_queue_method$min") == 0 || + strcmp(name, "$ivl_queue_method$max") == 0) { + const char* opname = strcmp(name, "$ivl_queue_method$min") == 0 + ? "min" + : "max"; + 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/%s/prop/v %u, %u;\n", opname, pidx, + elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 1;\n"); + } else { + ivl_signal_t sig = ivl_expr_signal(arg); + fprintf(vvp_out, " %%queue/%s/v v%p_0, %u;\n", opname, 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 if (strcmp(name, "$ivl_queue_method$min_with") == 0) + mode = 6; + else if (strcmp(name, "$ivl_queue_method$max_with") == 0) + mode = 7; + else if (strcmp(name, "$ivl_queue_method$unique_with") == 0) + mode = 8; + else if (strcmp(name, "$ivl_queue_method$unique_index_with") == 0) + mode = 9; + else if (strcmp(name, "$ivl_queue_method$sum_with") == 0) + mode = 10; + else if (strcmp(name, "$ivl_queue_method$product_with") == 0) + mode = 11; + else + return 1; + + 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 || mode == 6 || mode == 7 || + mode == 8 || mode == 9 || mode == 10 || mode == 11) { + 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); + + if (mode == 10 || mode == 11) { + draw_eval_vec4(pred); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else { + 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 == 6 || mode == 7) { + 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_nom); + } else if (mode == 8) { + 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_nom); + } else if (mode == 9) { + fprintf(vvp_out, " %%push/ix/vec4 %u, 32, 1;\n", i_reg); + fprintf(vvp_out, " %%queue/append_word/v 32;\n"); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else if (mode == 10 || mode == 11) { + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else if (mode == 2) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + 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) { + /* Keep result queue object, drop class object beneath it. */ + fprintf(vvp_out, " %%pop/obj 1, 1;\n"); + } else if (mode == 10) { + fprintf(vvp_out, " %%queue/sum/obj/v %u;\n", elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else if (mode == 11) { + fprintf(vvp_out, " %%queue/product/obj/v %u;\n", elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + } else if (mode == 8 || mode == 9) { + fprintf(vvp_out, " %%queue/unique/obj/v %u;\n", + mode == 9 ? 32 : elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 1;\n"); + } else if (mode == 6 || mode == 7) { + fprintf(vvp_out, " %%queue/%s/obj/v %u;\n", + mode == 6 ? "min" : "max", elem_wid); + fprintf(vvp_out, " %%pop/obj 1, 1;\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 || mode == 6 || mode == 7 || + mode == 8 || mode == 9 || mode == 10 || mode == 11) { + 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); + + if (mode == 10 || mode == 11) { + draw_eval_vec4(pred); + fprintf(vvp_out, " %%queue/append_word/v %u;\n", elem_wid); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else { + 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 == 6 || mode == 7) { + 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_nom); + } else if (mode == 8) { + 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_nom); + } else if (mode == 9) { + fprintf(vvp_out, " %%push/ix/vec4 %u, 32, 1;\n", i_reg); + fprintf(vvp_out, " %%queue/append_word/v 32;\n"); + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else if (mode == 10 || mode == 11) { + fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_nom); + } else if (mode == 2) { + fprintf(vvp_out, " %%queue/new_empty/v;\n"); + 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 == 10) { + fprintf(vvp_out, " %%queue/sum/obj/v %u;\n", elem_wid); + } else if (mode == 11) { + fprintf(vvp_out, " %%queue/product/obj/v %u;\n", elem_wid); + } else if (mode == 8 || mode == 9) { + fprintf(vvp_out, " %%queue/unique/obj/v %u;\n", + mode == 9 ? 32 : elem_wid); + } else if (mode == 6 || mode == 7) { + fprintf(vvp_out, " %%queue/%s/obj/v %u;\n", + mode == 6 ? "min" : "max", elem_wid); + } else 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, 1;\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, 1;\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, 1;\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, 1;\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, 1;\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, 1;\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 +885,29 @@ int draw_eval_object(ivl_expr_t ex) case IVL_EX_UFUNC: return eval_object_ufunc(ex); + case IVL_EX_ARRAY_PATTERN: + return eval_array_pattern_object(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_real.c b/tgt-vvp/eval_real.c index a61ab0f39..bb662033f 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -277,6 +277,15 @@ static void real_ex_pop(ivl_expr_t expr) fb = "f"; arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/r %u;\n", fb, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/real v%p_0;\n", fb, ivl_expr_signal(arg)); diff --git a/tgt-vvp/eval_string.c b/tgt-vvp/eval_string.c index 2624c43f4..303df6ecd 100644 --- a/tgt-vvp/eval_string.c +++ b/tgt-vvp/eval_string.c @@ -167,6 +167,15 @@ static void string_ex_pop(ivl_expr_t expr) fb = "f"; arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/str %u;\n", fb, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/str v%p_0;\n", fb, ivl_expr_signal(arg)); diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 6bbbb0c0a..1f987a076 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -917,6 +917,28 @@ static void draw_property_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); unsigned pidx = ivl_expr_property_idx(expr); + /* Queue/dynamic-array property with index: index is oper1 (see + * dll_target::expr_property). */ + ivl_expr_t index_ex = ivl_expr_oper1(expr); + + if (index_ex) { + ivl_type_t cls_type = ivl_signal_net_type(sig); + ivl_type_t ptype = ivl_type_prop_type(cls_type, pidx); + if (ptype) { + ivl_variable_type_t pbase = ivl_type_base(ptype); + if (pbase == IVL_VT_QUEUE || pbase == IVL_VT_DARRAY) { + unsigned wid = ivl_expr_width(expr); + draw_eval_expr_into_integer(index_ex, 3); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", + pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (ivl_expr_value(expr) == IVL_VT_BOOL) + fprintf(vvp_out, " %%cast2;\n"); + return; + } + } + } fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%prop/v %u;\n", pidx); @@ -948,7 +970,30 @@ static void draw_select_vec4(ivl_expr_t expr) return; } - if (ivl_expr_value(subexpr)==IVL_VT_DARRAY) { + /* Class property that is a queue or dynamic array: c.q[idx] */ + if (ivl_expr_type(subexpr) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(subexpr); + unsigned pidx = ivl_expr_property_idx(subexpr); + ivl_type_t cls_type = ivl_signal_net_type(clas); + ivl_type_t ptype = ivl_type_prop_type(cls_type, pidx); + if (ptype) { + ivl_variable_type_t pbase = ivl_type_base(ptype); + if (pbase == IVL_VT_QUEUE || pbase == IVL_VT_DARRAY) { + assert(base); + draw_eval_expr_into_integer(base, 3); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%load/prop/dar/vec4 %u, %u;\n", + pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (ivl_expr_value(expr) == IVL_VT_BOOL) + fprintf(vvp_out, " %%cast2;\n"); + return; + } + } + } + + if (ivl_expr_value(subexpr)==IVL_VT_DARRAY || + ivl_expr_value(subexpr)==IVL_VT_QUEUE) { ivl_signal_t sig = ivl_expr_signal(subexpr); assert(sig); assert( (ivl_signal_data_type(sig)==IVL_VT_DARRAY) @@ -1001,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; @@ -1017,6 +1056,16 @@ static void draw_darray_pop(ivl_expr_t expr) fb = "f"; ivl_expr_t arg = ivl_expr_parm(expr, 0); + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%qpop/prop/%s/v %u, %u;\n", fb, pidx, + ivl_expr_width(expr)); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); fprintf(vvp_out, " %%qpop/%s/v v%p_0, %u;\n", fb, ivl_expr_signal(arg), @@ -1049,6 +1098,49 @@ 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) { + ivl_signal_t sig = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); + fprintf(vvp_out, " %%prop/queue/size %u;\n", pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + } + + if ((strcmp(ivl_expr_name(expr), "$ivl_queue_method$sum") == 0 || + strcmp(ivl_expr_name(expr), "$ivl_queue_method$product") == 0) && + parm_count == 1) { + ivl_expr_t arg = ivl_expr_parm(expr, 0); + unsigned wid = ivl_expr_width(expr); + const char* op = strcmp(ivl_expr_name(expr), "$ivl_queue_method$product") == 0 + ? "product" + : "sum"; + if (ivl_expr_type(arg) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(arg); + unsigned pidx = ivl_expr_property_idx(arg); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%queue/%s/prop/v %u, %u;\n", op, pidx, wid); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return; + } + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); + fprintf(vvp_out, " %%queue/%s/v v%p_0, %u;\n", + op, ivl_expr_signal(arg), wid); + return; + } + draw_vpi_func_call(expr); } @@ -1357,7 +1449,9 @@ void draw_eval_vec4(ivl_expr_t expr) } assert(ivl_expr_value(expr) == IVL_VT_BOOL || - ivl_expr_value(expr) == IVL_VT_VECTOR); + ivl_expr_value(expr) == IVL_VT_VECTOR || + (ivl_expr_type(expr) == IVL_EX_SELECT && ivl_expr_oper2(expr) != 0) || + ivl_expr_type(expr) == IVL_EX_PROPERTY); switch (ivl_expr_type(expr)) { case IVL_EX_BINARY: diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index ac0a92dd3..75e687441 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -1364,15 +1364,16 @@ static int show_stmt_assign_sig_cobject(ivl_statement_t net) fprintf(vvp_out, " %%store/prop/str %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY) { + } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY + || ivl_type_base(prop_type) == IVL_VT_QUEUE) { int idx = 0; - /* The property is a darray, and there is no mux - expression to the assignment is of an entire - array object. */ + /* The property is a darray or queue, and there is no mux + expression so the assignment is of an entire + array/queue object. */ errors += draw_eval_object(rval); - fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY\n", prop_idx, idx); + fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY or QUEUE\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_CLASS) { 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/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 2604ae656..a29129fb3 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1663,6 +1663,24 @@ static int show_delete_method(ivl_statement_t net) return 1; ivl_expr_t parm = ivl_stmt_parm(net, 0); + if (ivl_expr_type(parm) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm); + unsigned pidx = ivl_expr_property_idx(parm); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t queue_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(queue_type) == IVL_VT_QUEUE); + + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + if (parm_count == 2) { + draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3); + fprintf(vvp_out, " %%delete/prop/elem %u;\n", pidx); + } else { + fprintf(vvp_out, " %%delete/prop/obj %u;\n", pidx); + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return 0; + } + assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm); @@ -1673,11 +1691,72 @@ static int show_delete_method(ivl_statement_t net) draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3); fprintf(vvp_out, " %%delete/elem v%p_0;\n", var); } else { - fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); + fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); } return 0; } +static int show_reverse_method(ivl_statement_t net) +{ + show_stmt_file_line(net, "reverse dynamic array or queue"); + + unsigned parm_count = ivl_stmt_parm_count(net); + if (parm_count != 1) + return 1; + + ivl_expr_t parm = ivl_stmt_parm(net, 0); + if (ivl_expr_type(parm) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm); + unsigned pidx = ivl_expr_property_idx(parm); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t arr_type = ivl_type_prop_type(sig_type, pidx); + ivl_variable_type_t bt = ivl_type_base(arr_type); + if (bt != IVL_VT_QUEUE && bt != IVL_VT_DARRAY) + return 1; + + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%reverse/prop/obj %u;\n", pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return 0; + } + + assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); + ivl_signal_t var = ivl_expr_signal(parm); + fprintf(vvp_out, " %%reverse/obj v%p_0;\n", var); + return 0; +} + +static int show_array_order_method(ivl_statement_t net, const char*descr, + const char*op_signal, const char*op_prop) +{ + show_stmt_file_line(net, descr); + + unsigned parm_count = ivl_stmt_parm_count(net); + if (parm_count != 1) + return 1; + + ivl_expr_t parm = ivl_stmt_parm(net, 0); + if (ivl_expr_type(parm) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm); + unsigned pidx = ivl_expr_property_idx(parm); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t arr_type = ivl_type_prop_type(sig_type, pidx); + ivl_variable_type_t bt = ivl_type_base(arr_type); + if (bt != IVL_VT_QUEUE && bt != IVL_VT_DARRAY) + return 1; + + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %s %u;\n", op_prop, pidx); + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + return 0; + } + + assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); + ivl_signal_t var = ivl_expr_signal(parm); + fprintf(vvp_out, " %s v%p_0;\n", op_signal, var); + return 0; +} + static int show_insert_method(ivl_statement_t net) { show_stmt_file_line(net, "queue: insert"); @@ -1687,6 +1766,47 @@ static int show_insert_method(ivl_statement_t net) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); + if (ivl_expr_type(parm0) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm0); + unsigned pidx = ivl_expr_property_idx(parm0); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t var_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, + ivl_type_queue_max(var_type)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + draw_eval_expr_into_integer(parm1, 3); + ivl_expr_t parm2 = ivl_stmt_parm(net,2); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm2); + fprintf(vvp_out, " %%qinsert/prop/r %u, %d;\n", + pidx, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm2); + fprintf(vvp_out, " %%qinsert/prop/str %u, %d;\n", + pidx, idx); + break; + default: + draw_eval_vec4(parm2); + fprintf(vvp_out, " %%qinsert/prop/v %u, %d, %u;\n", + pidx, idx, + ivl_type_packed_width(element_type)); + break; + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + clr_word(idx); + return 0; + } + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); @@ -1740,6 +1860,45 @@ static int show_push_frontback_method(ivl_statement_t net, bool is_front) return 1; ivl_expr_t parm0 = ivl_stmt_parm(net,0); + if (ivl_expr_type(parm0) == IVL_EX_PROPERTY) { + ivl_signal_t clas = ivl_expr_signal(parm0); + unsigned pidx = ivl_expr_property_idx(parm0); + ivl_type_t sig_type = ivl_signal_net_type(clas); + ivl_type_t var_type = ivl_type_prop_type(sig_type, pidx); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + fprintf(vvp_out, " %%load/obj v%p_0;\n", clas); + fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, + ivl_type_queue_max(var_type)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm1); + fprintf(vvp_out, " %%store/prop/%s/r %u, %d;\n", + type_code, pidx, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm1); + fprintf(vvp_out, " %%store/prop/%s/str %u, %d;\n", + type_code, pidx, idx); + break; + default: + draw_eval_vec4(parm1); + fprintf(vvp_out, " %%store/prop/%s/v %u, %d, %u;\n", + type_code, pidx, idx, + ivl_type_packed_width(element_type)); + break; + } + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + clr_word(idx); + return 0; + } + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); @@ -1783,6 +1942,21 @@ static int show_system_task_call(ivl_statement_t net) if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0) return show_delete_method(net); + if (strcmp(stmt_name,"$ivl_darray_method$reverse") == 0) + return show_reverse_method(net); + + if (strcmp(stmt_name,"$ivl_darray_method$sort") == 0) + return show_array_order_method(net, "sort dynamic array or queue", + "%sort/obj", "%sort/prop/obj"); + + if (strcmp(stmt_name,"$ivl_darray_method$rsort") == 0) + return show_array_order_method(net, "rsort dynamic array or queue", + "%rsort/obj", "%rsort/prop/obj"); + + if (strcmp(stmt_name,"$ivl_darray_method$shuffle") == 0) + return show_array_order_method(net, "shuffle dynamic array or queue", + "%shuffle/obj", "%shuffle/prop/obj"); + if (strcmp(stmt_name,"$ivl_queue_method$insert") == 0) return show_insert_method(net); diff --git a/vpi/libvpi.c b/vpi/libvpi.c index d82422586..d88254257 100644 --- a/vpi/libvpi.c +++ b/vpi/libvpi.c @@ -286,6 +286,11 @@ void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit) assert(vpip_routines); vpip_routines->format_strength(str, value, bit); } +char* vpip_format_pretty(vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->format_pretty(ref); +} void vpip_make_systf_system_defined(vpiHandle ref) { assert(vpip_routines); diff --git a/vpi/sys_display.c b/vpi/sys_display.c index dad17f127..155ec3169 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -867,6 +867,35 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus, * be a binary string (can contain NULLs). */ break; + case 'p': + case 'P': + *idx += 1; + if (plus != 0 || prec != -1) { + vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } + if (*idx >= info->nitems) { + vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } else { + char *pp = vpip_format_pretty(info->items[*idx]); + if (pp == 0) { + vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", + info->filename, info->lineno, info->name, fmtb); + } else { + char *cp = pp; + if (width == -1) width = 0; + size = strlen(cp) + 1; + if ((signed)size < (width+1)) size = width+1; + if (size > ini_size) result = realloc(result, size*sizeof(char)); + if (ljust == 0) sprintf(result, "%*s", width, cp); + else sprintf(result, "%-*s", width, cp); + free(pp); + size = strlen(result) + 1; + } + } + break; + default: vpi_printf("WARNING: %s:%d: unknown format %s%s.\n", info->filename, info->lineno, info->name, fmtb); @@ -1215,6 +1244,7 @@ static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, #endif case vpiClassVar: case vpiSysFuncCall: + case vpiRegArray: /* dynamic arrays, queues, SV array vars */ break; default: diff --git a/vpi_user.h b/vpi_user.h index c347fe15e..9fcfd6342 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -641,6 +641,10 @@ extern DLLEXPORT void (*vlog_startup_routines[])(void); /* Format a scalar a la %v. The str points to a 4byte character buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); + /* Pretty-print a dynamic array or queue (including class property queues) + * for %p. Returns a malloc'd string, or NULL if the handle is not + * supported. Caller must free the returned pointer when non-NULL. */ +extern char* vpip_format_pretty(vpiHandle ref); /* Set the return value to return from the vvp run time. This is usually 0 or 1. This is the exit code that the vvp process returns, and in distinct from the finish_number that is an @@ -696,7 +700,7 @@ extern void vpip_count_drivers(vpiHandle ref, unsigned idx, */ // Increment the version number any time vpip_routines_s is changed. -static const PLI_UINT32 vpip_routines_version = 1; +static const PLI_UINT32 vpip_routines_version = 2; typedef struct { vpiHandle (*register_cb)(p_cb_data); @@ -737,6 +741,7 @@ typedef struct { s_vpi_vecval(*calc_clog2)(vpiHandle); void (*count_drivers)(vpiHandle, unsigned, unsigned [4]); void (*format_strength)(char*, s_vpi_value*, unsigned); + char* (*format_pretty)(vpiHandle); void (*make_systf_system_defined)(vpiHandle); void (*mcd_rawwrite)(PLI_UINT32, const char*, size_t); void (*set_return_value)(int); diff --git a/vvp/codes.h b/vvp/codes.h index 94a4f26b5..e9e483674 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -150,6 +150,7 @@ extern bool of_LOAD_REAL(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_R(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_STR(vthread_t thr, vvp_code_t code); extern bool of_LOAD_DAR_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_LOAD_PROP_DAR_VEC4(vthread_t thr, vvp_code_t code); extern bool of_LOAD_OBJ(vthread_t thr, vvp_code_t code); extern bool of_LOAD_OBJA(vthread_t thr, vvp_code_t code); extern bool of_LOAD_STR(vthread_t thr, vvp_code_t code); @@ -212,6 +213,14 @@ extern bool of_REPLICATE(vthread_t thr, vvp_code_t code); extern bool of_RET_REAL(vthread_t thr, vvp_code_t code); extern bool of_RET_STR(vthread_t thr, vvp_code_t code); extern bool of_RET_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_REVERSE_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_REVERSE_PROP_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_RSORT_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_RSORT_PROP_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_SHUFFLE_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_SHUFFLE_PROP_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_SORT_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_SORT_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t code); extern bool of_RETLOAD_STR(vthread_t thr, vvp_code_t code); extern bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t code); @@ -232,6 +241,62 @@ extern bool of_STORE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QB_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_PROP_QF_V(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_R(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_STR(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_REAL(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_STR(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_PROP_B_V(vthread_t thr, vvp_code_t code); +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_PRODUCT_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_PRODUCT_OBJ_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_PRODUCT_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_SIZE_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_SUM_OBJ_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_SUM_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_SUM_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_MIN_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MIN_OBJ_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MIN_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MAX_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MAX_OBJ_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_MAX_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_UNIQUE_OBJ_V(vthread_t thr, vvp_code_t code); +extern bool of_QUEUE_UNIQUE_INDEX_OBJ_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); extern bool of_STORE_QB_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_QB_V(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 5c0460086..2831efe40 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -48,9 +48,13 @@ unsigned compile_errors = 0; /* * The opcode table lists all the code mnemonics, along with their - * opcode and operand types. The table is written sorted by mnemonic - * so that it can be searched by binary search. The opcode_compare - * function is a helper function for that lookup. + * opcode and operand types. The table must be sorted lexicographically + * by mnemonic string: compile_code() uses bsearch() on this array. + * If the order is wrong, lookup fails and the assembler reports + * "Invalid opcode" for otherwise valid instructions (e.g. class + * property queue ops must sort among all %delete/*, %qpop/*, %store/* + * names, not grouped by feature). + * The opcode_compare function is a helper for that lookup. */ enum operand_e { @@ -135,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} }, @@ -151,6 +157,8 @@ static const struct opcode_table_s opcode_table[] = { { "%delayx", of_DELAYX, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%delete/elem",of_DELETE_ELEM,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%delete/prop/elem", of_DELETE_PROP_ELEM, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%delete/prop/obj", of_DELETE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%delete/tail",of_DELETE_TAIL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%disable", of_DISABLE, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, { "%disable/flow", of_DISABLE_FLOW, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, @@ -204,6 +212,7 @@ static const struct opcode_table_s opcode_table[] = { { "%load/dar/vec4",of_LOAD_DAR_VEC4,1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%load/obj", of_LOAD_OBJ, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%load/obja", of_LOAD_OBJA, 2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, + { "%load/prop/dar/vec4", of_LOAD_PROP_DAR_VEC4, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, { "%load/real", of_LOAD_REAL, 1,{OA_VPI_PTR, OA_NONE, OA_NONE} }, { "%load/str", of_LOAD_STR, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%load/stra", of_LOAD_STRA, 2,{OA_ARR_PTR, OA_BIT1, OA_NONE} }, @@ -241,14 +250,19 @@ static const struct opcode_table_s opcode_table[] = { { "%pow/s", of_POW_S, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%pow/wr", of_POW_WR, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%prop/obj",of_PROP_OBJ,2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%prop/queue/size", of_PROP_QUEUE_SIZE, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%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} }, { "%pushv/str", of_PUSHV_STR, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%putc/str/vec4",of_PUTC_STR_VEC4,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qinsert/prop/r", of_QINSERT_PROP_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qinsert/prop/str", of_QINSERT_PROP_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%qinsert/prop/v", of_QINSERT_PROP_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%qinsert/real",of_QINSERT_REAL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%qinsert/str", of_QINSERT_STR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%qinsert/v", of_QINSERT_V, 3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, @@ -258,6 +272,47 @@ static const struct opcode_table_s opcode_table[] = { { "%qpop/f/real",of_QPOP_F_REAL,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%qpop/f/str", of_QPOP_F_STR, 1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%qpop/f/v", of_QPOP_F_V, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qpop/prop/b/r", of_QPOP_PROP_B_REAL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/b/str", of_QPOP_PROP_B_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%qpop/prop/b/v", of_QPOP_PROP_B_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%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/max/obj/v", of_QUEUE_MAX_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} }, + { "%queue/max/prop/v", of_QUEUE_MAX_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/max/v", of_QUEUE_MAX_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/min/obj/v", of_QUEUE_MIN_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} }, + { "%queue/min/prop/v", of_QUEUE_MIN_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/min/v", of_QUEUE_MIN_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/new_empty/v", of_QUEUE_NEW_EMPTY_V, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%queue/product/obj/v", of_QUEUE_PRODUCT_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} }, + { "%queue/product/prop/v", of_QUEUE_PRODUCT_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/product/v", of_QUEUE_PRODUCT_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/size/v", of_QUEUE_SIZE_V, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%queue/sum/obj/v", of_QUEUE_SUM_OBJ_V, 1, {OA_BIT1, OA_NONE, OA_NONE} }, + { "%queue/sum/prop/v", of_QUEUE_SUM_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%queue/sum/v", of_QUEUE_SUM_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%queue/unique/index/obj/v", of_QUEUE_UNIQUE_INDEX_OBJ_V, 1, {OA_BIT1, 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/obj/v", of_QUEUE_UNIQUE_OBJ_V, 1, {OA_BIT1, OA_NONE, 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} }, @@ -268,6 +323,10 @@ static const struct opcode_table_s opcode_table[] = { { "%retload/real",of_RETLOAD_REAL,1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%retload/str", of_RETLOAD_STR, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%retload/vec4",of_RETLOAD_VEC4,1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%reverse/obj", of_REVERSE_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%reverse/prop/obj", of_REVERSE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%rsort/obj", of_RSORT_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%rsort/prop/obj", of_RSORT_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} }, { "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} }, @@ -275,6 +334,10 @@ static const struct opcode_table_s opcode_table[] = { { "%shiftl", of_SHIFTL, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%shiftr", of_SHIFTR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%shiftr/s", of_SHIFTR_S, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%shuffle/obj", of_SHUFFLE_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%shuffle/prop/obj", of_SHUFFLE_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%sort/obj", of_SORT_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%sort/prop/obj", of_SORT_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%split/vec4", of_SPLIT_VEC4, 1,{OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/dar/r", of_STORE_DAR_R, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%store/dar/str", of_STORE_DAR_STR, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} }, @@ -282,6 +345,12 @@ static const struct opcode_table_s opcode_table[] = { { "%store/obj", of_STORE_OBJ, 1, {OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%store/obja", of_STORE_OBJA, 2, {OA_ARR_PTR, OA_BIT1, OA_NONE} }, { "%store/prop/obj",of_STORE_PROP_OBJ,2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/r", of_STORE_PROP_QB_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/str", of_STORE_PROP_QB_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qb/v", of_STORE_PROP_QB_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, + { "%store/prop/qf/r", of_STORE_PROP_QF_R, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qf/str", of_STORE_PROP_QF_STR, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, + { "%store/prop/qf/v", of_STORE_PROP_QF_V, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, { "%store/prop/r", of_STORE_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/str",of_STORE_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/v", of_STORE_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, diff --git a/vvp/compile.h b/vvp/compile.h index d19263ae5..6abc26439 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -344,6 +344,9 @@ class resolv_list_s { */ extern void functor_ref_lookup(vvp_net_t**ref, char*lab); +extern vpiHandle vpip_make_prop_queue_ref(char* class_label, unsigned prop_idx, + unsigned is_queue_flag); + /* * This function schedules a lookup of the labeled instruction. The * code points to a code structure that points to the instruction diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 1a6ce88ef..122bcaf45 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -272,6 +272,7 @@ inline uint64_t strtouint64(const char*str, char**endptr, int base) "&A" { return K_A; } "&APV" { return K_APV; } +"&PQ" { return K_PQ; } "&PV" { return K_PV; } "%"[.$_/a-zA-Z0-9]+ { diff --git a/vvp/parse.y b/vvp/parse.y index bd8dba1bf..e890070d8 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -93,7 +93,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U %token K_NET8 K_NET8_2S K_NET8_2U K_NET8_S %token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV -%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR +%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PQ K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR %token K_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT %token K_RESOLV K_RTRAN K_RTRANIF0 K_RTRANIF1 %token K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS @@ -1122,6 +1122,8 @@ symbol_access { $$ = vpip_make_PV($3, $5, $7); } | K_APV '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER ',' T_NUMBER '>' { $$ = vpip_make_vthr_APV($3, $5, $7, $9); } + | K_PQ '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER '>' + { $$ = vpip_make_prop_queue_ref($3, $5, $7); } ; /* functor operands can only be a list of symbols. */ diff --git a/vvp/vpi_darray.cc b/vvp/vpi_darray.cc index db9b902c6..d6b636463 100644 --- a/vvp/vpi_darray.cc +++ b/vvp/vpi_darray.cc @@ -22,6 +22,7 @@ # include "vpi_priv.h" # include "vvp_net_sig.h" # include "vvp_darray.h" +# include "vvp_cobject.h" # include "array_common.h" # include "schedule.h" #ifdef CHECK_WITH_VALGRIND @@ -294,38 +295,23 @@ vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net) } __vpiQueueVar::__vpiQueueVar(__vpiScope*sc, const char*na, vvp_net_t*ne) -: __vpiBaseVar(sc, na, ne) +: __vpiDarrayVar(sc, na, ne) { } -int __vpiQueueVar::get_type_code(void) const -{ return vpiArrayVar; } - - int __vpiQueueVar::vpi_get(int code) { - vvp_fun_signal_object*fun = dynamic_cast (get_net()->fun); - assert(fun); - vvp_object_t val = fun->get_object(); - const vvp_queue*aval = val.peek(); - switch (code) { case vpiArrayType: return vpiQueueArray; - case vpiSize: - if (aval == 0) - return 0; - else - return aval->get_size(); - default: - return 0; + return __vpiDarrayVar::vpi_get(code); } } void __vpiQueueVar::vpi_get_value(p_vpi_value val) { - val->format = vpiSuppressVal; + __vpiDarrayVar::vpi_get_value(val); } @@ -339,9 +325,140 @@ vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net) return obj; } +__vpiPropQueueRef::__vpiPropQueueRef(__vpiScope*scope, unsigned pidx, + bool is_queue) +: class_net_(0), prop_idx_(pidx), is_queue_(is_queue), scope_(scope) +{ +} + +int __vpiPropQueueRef::get_type_code(void) const +{ return vpiArrayVar; } + +static vvp_darray* get_live_darray_from_prop(vvp_net_t* class_net, unsigned pid) +{ + if (!class_net) + return 0; + vvp_fun_signal_object* fun + = dynamic_cast(class_net->fun); + if (!fun) + return 0; + vvp_object_t obj = fun->get_object(); + vvp_cobject* cobj = obj.peek(); + if (!cobj) + return 0; + vvp_object_t qobj; + cobj->get_object(pid, qobj, 0); + return qobj.peek(); +} + +int __vpiPropQueueRef::vpi_get(int code) +{ + vvp_darray* aobj = get_live_darray_from_prop(class_net_, prop_idx_); + switch (code) { + case vpiArrayType: + return is_queue_ ? vpiQueueArray : vpiDynamicArray; + case vpiLeftRange: + return 0; + case vpiRightRange: + return aobj ? (int) (aobj->get_size() - 1) : 0; + case vpiSize: + return aobj ? (int) aobj->get_size() : 0; + default: + return 0; + } +} + +char* __vpiPropQueueRef::vpi_get_str(int code) +{ + if (code == vpiFile) { + return simple_set_rbuf_str(file_names[0]); + } + return generic_get_str(code, scope_, "prop_darray", NULL); +} + +void __vpiPropQueueRef::vpi_get_value(p_vpi_value val) +{ + val->format = vpiSuppressVal; +} + +vpiHandle vpip_make_prop_queue_ref(char* class_label, unsigned prop_idx, + unsigned is_queue_flag) +{ + __vpiPropQueueRef* obj = new __vpiPropQueueRef(vpip_peek_current_scope(), + prop_idx, + is_queue_flag != 0); + functor_ref_lookup(&obj->class_net_, class_label); + return obj; +} + +static char* format_darray_pretty(vvp_darray* aobj) +{ + if (!aobj || aobj->get_size() == 0) + return strdup("{}"); + + string out = "{"; + + for (size_t i = 0; i < aobj->get_size(); i += 1) { + if (i > 0) + out += ", "; + if (dynamic_cast(aobj)) { + double d; + aobj->get_word((unsigned) i, d); + char buf[256]; + snprintf(buf, sizeof buf, "%g", d); + out += buf; + } else if (dynamic_cast(aobj)) { + string s; + aobj->get_word((unsigned) i, s); + out += "\""; + out += s; + out += "\""; + } else { + vvp_vector4_t v; + aobj->get_word((unsigned) i, v); + s_vpi_value val; + val.format = vpiDecStrVal; + vpip_vec4_get_value(v, v.size(), false, &val); + out += val.value.str; + } + } + + out += "}"; + return strdup(out.c_str()); +} + +extern "C" char* vpip_format_pretty(vpiHandle ref) +{ + if (!ref) + return 0; + + if (__vpiPropQueueRef* pr = dynamic_cast<__vpiPropQueueRef*>(ref)) { + vvp_darray* a = get_live_darray_from_prop(pr->class_net_, + pr->prop_idx_); + return format_darray_pretty(a); + } + + if (__vpiDarrayVar* dv = dynamic_cast<__vpiDarrayVar*>(ref)) { + vvp_fun_signal_object* fun + = dynamic_cast(dv->get_net()->fun); + if (!fun) + return 0; + vvp_object_t obj = fun->get_object(); + vvp_darray* aobj = obj.peek(); + return format_darray_pretty(aobj); + } + + return 0; +} + #ifdef CHECK_WITH_VALGRIND void array_delete(vpiHandle item) { + if (__vpiPropQueueRef* pq = dynamic_cast<__vpiPropQueueRef*>(item)) { + delete pq; + return; + } + __vpiDarrayVar*dobj = dynamic_cast<__vpiDarrayVar*>(item); if (dobj) { if (dobj->vals_words) delete [] (dobj->vals_words-1); @@ -349,12 +466,6 @@ void array_delete(vpiHandle item) return; } - __vpiQueueVar*qobj = dynamic_cast<__vpiQueueVar*>(item); - if (qobj) { - delete qobj; - return; - } - fprintf(stderr, "Need support for deleting array type: %d\n", item->vpi_get(vpiArrayType)); assert(0); } diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index c3aabdfa9..31d58e9dd 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -2059,6 +2059,7 @@ extern "C" void vpip_count_drivers(vpiHandle ref, unsigned idx, } #if defined(__MINGW32__) || defined (__CYGWIN__) +extern "C" char* vpip_format_pretty(vpiHandle ref); vpip_routines_s vpi_routines = { .register_cb = vpi_register_cb, .remove_cb = vpi_remove_cb, @@ -2098,6 +2099,7 @@ vpip_routines_s vpi_routines = { .calc_clog2 = vpip_calc_clog2, .count_drivers = vpip_count_drivers, .format_strength = vpip_format_strength, + .format_pretty = vpip_format_pretty, .make_systf_system_defined = vpip_make_systf_system_defined, .mcd_rawwrite = vpip_mcd_rawwrite, .set_return_value = vpip_set_return_value, diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 3b253c999..4486454cb 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -870,18 +870,38 @@ class __vpiDarrayVar : public __vpiBaseVar, public __vpiArrayBase { extern vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net); -class __vpiQueueVar : public __vpiBaseVar { +class __vpiQueueVar : public __vpiDarrayVar { public: __vpiQueueVar(__vpiScope*scope, const char*name, vvp_net_t*net); - int get_type_code(void) const override; int vpi_get(int code) override; void vpi_get_value(p_vpi_value val) override; }; extern vpiHandle vpip_make_queue_var(const char*name, vvp_net_t*net); +class __vpiPropQueueRef : public __vpiHandle { + + public: + explicit __vpiPropQueueRef(__vpiScope*scope, unsigned pidx, bool is_queue); + + int get_type_code(void) const override; + int vpi_get(int code) override; + char* vpi_get_str(int code) override; + void vpi_get_value(p_vpi_value val) override; + + vvp_net_t* class_net_; + unsigned prop_idx_; + bool is_queue_; + + private: + __vpiScope* scope_; +}; + +extern vpiHandle vpip_make_prop_queue_ref(char* class_label, unsigned prop_idx, + unsigned is_queue_flag); + class __vpiCobjectVar : public __vpiBaseVar { public: diff --git a/vvp/vthread.cc b/vvp/vthread.cc index de7578b00..a85f2c226 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -504,6 +504,60 @@ 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; +} + +static void get_queue_or_darray_vec4_from_object(vvp_object_t src_obj, + vvp_queue_vec4*& qsrc, + vvp_darray*& dsrc) +{ + qsrc = src_obj.peek(); + if (qsrc) { + dsrc = 0; + return; + } + vvp_darray* dany = src_obj.peek(); + if (dany && dynamic_cast(dany) == 0) { + dsrc = dany; + return; + } + qsrc = 0; + dsrc = 0; +} + /* * The following are used to allow a common template to be written for * queue real/string/vec4 operations @@ -2556,6 +2610,212 @@ bool of_DELETE_OBJ(vthread_t thr, vvp_code_t cp) return true; } +/* %reverse/obj