From 8994ef1483c1fb1d6b726b5b2d0c859a593b7738 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 20 Apr 2013 16:27:51 -0700 Subject: [PATCH] Implement class constructors. Class constructors are the "new" method in a class description. Elaborate the constructor as an ordinary method, but the only way to access this method is to implicitly call it. The elaborator will take the constructor call and generate a naked "new" expression and implicit constructor method call with the object itself as the return value. --- PExpr.cc | 10 +++++ PExpr.h | 3 ++ elab_expr.cc | 78 ++++++++++++++++++++++++++++++++-- elab_sig.cc | 43 ++++++++++++++----- parse.y | 6 ++- pform_dump.cc | 20 ++++++--- tgt-vvp/draw_ufunc.c | 98 ++++++++++++++++++++++++------------------- tgt-vvp/eval_object.c | 9 ++++ tgt-vvp/vvp_priv.h | 1 + 9 files changed, 201 insertions(+), 67 deletions(-) diff --git a/PExpr.cc b/PExpr.cc index 67f40bde5..9f20a33ca 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -426,6 +426,16 @@ PENewClass::PENewClass(void) { } +PENewClass::PENewClass(const list&p) +: parms_(p.size()) +{ + size_t tmp_idx = 0; + for (list::const_iterator cur = p.begin() + ; cur != p.end() ; ++ cur) { + parms_[tmp_idx++] = *cur; + } +} + PENewClass::~PENewClass() { } diff --git a/PExpr.h b/PExpr.h index 392ad2a01..69d1bc5fc 100644 --- a/PExpr.h +++ b/PExpr.h @@ -489,7 +489,9 @@ class PENewClass : public PExpr { public: explicit PENewClass (); + explicit PENewClass (const std::list&p); ~PENewClass(); + virtual void dump(ostream&) const; // Class objects don't have a useful width, but the expression // is IVL_VT_CLASS. @@ -502,6 +504,7 @@ class PENewClass : public PExpr { ivl_type_t type, unsigned flags) const; private: + std::vectorparms_; }; class PENull : public PExpr { diff --git a/elab_expr.cc b/elab_expr.cc index cf6ff341e..b3102573e 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -4448,12 +4448,82 @@ unsigned PENewClass::test_width(Design*, NetScope*, width_mode_t&) return 1; } -NetExpr* PENewClass::elaborate_expr(Design*, NetScope*, +NetExpr* PENewClass::elaborate_expr(Design*des, NetScope*scope, ivl_type_t ntype, unsigned) const { - NetENew*tmp = new NetENew(ntype); - tmp->set_line(*this); - return tmp; + NetENew*obj = new NetENew(ntype); + obj->set_line(*this); + + // Find the constructor for the class. If there is no + // constructor then the result of this expression is the + // allocation alone. + const netclass_t*ctype = dynamic_cast (ntype); + NetScope*new_scope = ctype->method_from_name(perm_string::literal("new")); + if (new_scope == 0) { + // No constructor. + if (parms_.size() > 0) { + cerr << get_fileline() << ": error: " + << "Class " << ctype->get_name() + << " has no constructor, but you passed " << parms_.size() + << " arguments to the new operator." << endl; + des->errors += 1; + } + return obj; + } + + NetFuncDef*def = new_scope->func_def(); + ivl_assert(*this, def); + + if ((parms_.size()+1) != def->port_count()) { + cerr << get_fileline() << ": error: Parm count mismatch" + << " passing " << parms_.size() << " arguments " + << " to constructor expecting " << (def->port_count()-1) + << " arguments." << endl; + des->errors += 1; + return obj; + } + + vector parms (1 + parms_.size()); + parms[0] = obj; + + int missing_parms = 0; + int parm_errors = 0; + for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { + PExpr*tmp = parms_[idx]; + size_t pidx = idx + 1; + + if (tmp == 0) { + parms[pidx] = 0; + missing_parms += 1; + continue; + } + + parms[pidx] = elaborate_rval_expr(des, scope, def->port(pidx)->data_type(), + def->port(pidx)->vector_width(), + tmp, false); + if (parms[pidx] == 0) { + parm_errors += 1; + continue; + } + } + + if (missing_parms > 0) { + cerr << get_fileline() << ": error: The " << scope_path(new_scope) + << " constructor call is missing arguments." << endl; + parm_errors += 1; + des->errors += 1; + } + + // The return value for the constructor is actually the "this" + // variable, instead of the "new" scope name. + NetNet*res = new_scope->find_signal(perm_string::literal("@")); + ivl_assert(*this, res); + + NetESignal*eres = new NetESignal(res); + NetEUFunc*con = new NetEUFunc(scope, new_scope, eres, parms, true); + con->set_line(*this); + + return con; } /* diff --git a/elab_sig.cc b/elab_sig.cc index fea0f837f..35e867426 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -537,20 +537,41 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const elaborate_sig_wires_(des, scope); - ivl_type_t ret_type; + NetNet*ret_sig; + if (gn_system_verilog() && fname == "new") { + // Special case: this is a constructor, so the return + // signal is also the first argument. For example, the + // source code for the definition may be: + // function new(...); + // endfunction + // In this case, the "@" port is the synthetic "this" + // argument and we also use it as a return value at the + // same time. + ret_sig = scope->find_signal(perm_string::literal("@")); + ivl_assert(*this, ret_sig); + + if (debug_elaborate) + cerr << get_fileline() << ": PFunction::elaborate_sig: " + << "Scope " << scope_path(scope) + << " is a CONSTRUCTOR, so use \"this\" argument" + << " as return value." << endl; - if (return_type_) { - ret_type = return_type_->elaborate_type(des, scope); } else { - netvector_t*tmp = new netvector_t(IVL_VT_LOGIC); - tmp->set_scalar(true); - ret_type = tmp; - } - list ret_unpacked; - NetNet*ret_sig = new NetNet(scope, fname, NetNet::REG, ret_unpacked, ret_type); + ivl_type_t ret_type; - ret_sig->set_line(*this); - ret_sig->port_type(NetNet::POUTPUT); + if (return_type_) { + ret_type = return_type_->elaborate_type(des, scope); + } else { + netvector_t*tmp = new netvector_t(IVL_VT_LOGIC); + tmp->set_scalar(true); + ret_type = tmp; + } + list ret_unpacked; + ret_sig = new NetNet(scope, fname, NetNet::REG, ret_unpacked, ret_type); + + ret_sig->set_line(*this); + ret_sig->port_type(NetNet::POUTPUT); + } vectorports; elaborate_sig_ports_(des, scope, ports); diff --git a/parse.y b/parse.y index ff2700f10..1cb8c8bf0 100644 --- a/parse.y +++ b/parse.y @@ -844,8 +844,10 @@ class_new /* IEEE1800-2005 A.2.4 */ $$ = tmp; } | K_new '(' expression_list_proper ')' - { yyerror(@1, "sorry: class_new not implemented yet."); - $$ = 0; + { PENewClass*tmp = new PENewClass(*$3); + FILE_NAME(tmp, @1); + delete $3; + $$ = tmp; } | K_new { PENewClass*tmp = new PENewClass; diff --git a/pform_dump.cc b/pform_dump.cc index eaca892a5..2e3eabfb5 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -291,7 +291,15 @@ void PENew::dump(ostream&out) const void PENewClass::dump(ostream&out) const { - out << "class_new"; + out << "class_new("; + if (parms_.size() > 0) { + parms_[0]->dump(out); + for (size_t idx = 1 ; idx < parms_.size() ; idx += 1) { + out << ", "; + parms_[idx]->dump(out); + } + } + out << ")"; } void PENull::dump(ostream&out) const @@ -874,17 +882,17 @@ void PForStatement::dump(ostream&out, unsigned ind) const void PFunction::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "function "; - if (is_auto_) out << "automatic..."; + if (is_auto_) out << "automatic "; + + out << pscope_name() << ";" << endl; + if (method_of()) + out << setw(ind) << "" << "method of " << method_of()->name << ";" << endl; if (return_type_) return_type_->pform_dump(out, ind+8); else out << setw(ind+8) << "" << "" << endl; - out << pscope_name() << ";" << endl; - if (method_of()) - out << setw(ind) << "" << "method of " << method_of()->name << ";" << endl; - dump_ports_(out, ind); dump_parameters_(out, ind); diff --git a/tgt-vvp/draw_ufunc.c b/tgt-vvp/draw_ufunc.c index 3755158bb..83f4d2137 100644 --- a/tgt-vvp/draw_ufunc.c +++ b/tgt-vvp/draw_ufunc.c @@ -88,25 +88,10 @@ static void draw_function_argument(ivl_signal_t port, ivl_expr_t expr) } } -/* - * A call to a user defined function generates a result that is the - * result of this expression. - * - * The result of the function is placed by the function execution into - * a signal within the scope of the function that also has a basename - * the same as the function. The ivl_target API handled the result - * mapping already, and we get the name of the result signal as - * parameter 0 of the function definition. - */ - -struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) +static void draw_ufunc_preamble(ivl_expr_t expr) { - unsigned idx; - unsigned swid = ivl_expr_width(expr); ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); - struct vector_info res; - unsigned load_wid; + unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { @@ -122,12 +107,45 @@ struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) draw_function_argument(port, ivl_expr_parm(expr, idx)); } - /* Call the function */ fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); +} + +static void draw_ufunc_epilogue(ivl_expr_t expr) +{ + ivl_scope_t def = ivl_expr_def(expr); + + /* If this is an automatic function, free the local storage. */ + if (ivl_scope_is_auto(def)) { + fprintf(vvp_out, " %%free S_%p;\n", def); + } +} + +/* + * A call to a user defined function generates a result that is the + * result of this expression. + * + * The result of the function is placed by the function execution into + * a signal within the scope of the function that also has a basename + * the same as the function. The ivl_target API handled the result + * mapping already, and we get the name of the result signal as + * parameter 0 of the function definition. + */ + +struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) +{ + unsigned swid = ivl_expr_width(expr); + ivl_scope_t def = ivl_expr_def(expr); + ivl_signal_t retval = ivl_scope_port(def, 0); + struct vector_info res; + unsigned load_wid; + + /* Take in arguments to function and call function code. */ + draw_ufunc_preamble(expr); + /* Fresh basic block starts after the join. */ clear_expression_lookaside(); @@ -159,11 +177,7 @@ struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) if (load_wid < wid) pad_expr_in_place(expr, res, swid); - /* If this is an automatic function, free the local storage. */ - if (ivl_scope_is_auto(def)) { - fprintf(vvp_out, " %%free S_%p;\n", def); - } - + draw_ufunc_epilogue(expr); return res; } @@ -171,24 +185,9 @@ void draw_ufunc_real(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); - unsigned idx; - /* If this is an automatic function, allocate the local storage. */ - if (ivl_scope_is_auto(def)) { - fprintf(vvp_out, " %%alloc S_%p;\n", def); - } - - assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); - for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { - ivl_signal_t port = ivl_scope_port(def, idx+1); - draw_function_argument(port, ivl_expr_parm(expr, idx)); - } - - - /* Call the function */ - fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); - fprintf(vvp_out, ", S_%p;\n", def); - fprintf(vvp_out, " %%join;\n"); + /* Take in arguments to function and call the function code. */ + draw_ufunc_preamble(expr); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); @@ -196,9 +195,20 @@ void draw_ufunc_real(ivl_expr_t expr) /* Load the result into a word. */ fprintf(vvp_out, " %%load/real v%p_0;\n", retval); - /* If this is an automatic function, free the local storage. */ - if (ivl_scope_is_auto(def)) { - fprintf(vvp_out, " %%free S_%p;\n", def); - } + draw_ufunc_epilogue(expr); } + +void draw_ufunc_object(ivl_expr_t expr) +{ + ivl_scope_t def = ivl_expr_def(expr); + ivl_signal_t retval = ivl_scope_port(def, 0); + + /* Take in arguments to function and call the function code. */ + draw_ufunc_preamble(expr); + + /* Load the result into the object stack. */ + fprintf(vvp_out, " %%load/obj v%p_0;\n", retval); + + draw_ufunc_epilogue(expr); +} diff --git a/tgt-vvp/eval_object.c b/tgt-vvp/eval_object.c index e50f0af16..02c7dbe63 100644 --- a/tgt-vvp/eval_object.c +++ b/tgt-vvp/eval_object.c @@ -97,6 +97,12 @@ static int eval_object_signal(ivl_expr_t ex) return 0; } +static int eval_object_ufunc(ivl_expr_t ex) +{ + draw_ufunc_object(ex); + return 0; +} + int draw_eval_object(ivl_expr_t ex) { switch (ivl_expr_type(ex)) { @@ -122,6 +128,9 @@ int draw_eval_object(ivl_expr_t ex) case IVL_EX_SIGNAL: return eval_object_signal(ex); + case IVL_EX_UFUNC: + return eval_object_ufunc(ex); + default: fprintf(vvp_out, "; ERROR: draw_eval_object: Invalid expression type %u\n", ivl_expr_type(ex)); return 1; diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 3c4800fe2..e94a2364b 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -101,6 +101,7 @@ extern void draw_lpm_mux(ivl_lpm_t net); extern struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid); extern void draw_ufunc_real(ivl_expr_t expr); +extern void draw_ufunc_object(ivl_expr_t expr); extern void pad_expr_in_place(ivl_expr_t expr, struct vector_info res, unsigned swid);