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.
This commit is contained in:
Stephen Williams 2013-04-20 16:27:51 -07:00
parent 20ee350601
commit 8994ef1483
9 changed files with 201 additions and 67 deletions

View File

@ -426,6 +426,16 @@ PENewClass::PENewClass(void)
{
}
PENewClass::PENewClass(const list<PExpr*>&p)
: parms_(p.size())
{
size_t tmp_idx = 0;
for (list<PExpr*>::const_iterator cur = p.begin()
; cur != p.end() ; ++ cur) {
parms_[tmp_idx++] = *cur;
}
}
PENewClass::~PENewClass()
{
}

View File

@ -489,7 +489,9 @@ class PENewClass : public PExpr {
public:
explicit PENewClass ();
explicit PENewClass (const std::list<PExpr*>&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::vector<PExpr*>parms_;
};
class PENull : public PExpr {

View File

@ -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<const netclass_t*> (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<NetExpr*> 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;
}
/*

View File

@ -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<netrange_t> 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<netrange_t> 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);
}
vector<NetNet*>ports;
elaborate_sig_ports_(des, scope, ports);

View File

@ -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;

View File

@ -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) << "" << "<implicit type>" << 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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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);