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:
parent
20ee350601
commit
8994ef1483
10
PExpr.cc
10
PExpr.cc
|
|
@ -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()
|
PENewClass::~PENewClass()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
PExpr.h
3
PExpr.h
|
|
@ -489,7 +489,9 @@ class PENewClass : public PExpr {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PENewClass ();
|
explicit PENewClass ();
|
||||||
|
explicit PENewClass (const std::list<PExpr*>&p);
|
||||||
~PENewClass();
|
~PENewClass();
|
||||||
|
|
||||||
virtual void dump(ostream&) const;
|
virtual void dump(ostream&) const;
|
||||||
// Class objects don't have a useful width, but the expression
|
// Class objects don't have a useful width, but the expression
|
||||||
// is IVL_VT_CLASS.
|
// is IVL_VT_CLASS.
|
||||||
|
|
@ -502,6 +504,7 @@ class PENewClass : public PExpr {
|
||||||
ivl_type_t type, unsigned flags) const;
|
ivl_type_t type, unsigned flags) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::vector<PExpr*>parms_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PENull : public PExpr {
|
class PENull : public PExpr {
|
||||||
|
|
|
||||||
78
elab_expr.cc
78
elab_expr.cc
|
|
@ -4448,12 +4448,82 @@ unsigned PENewClass::test_width(Design*, NetScope*, width_mode_t&)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetExpr* PENewClass::elaborate_expr(Design*, NetScope*,
|
NetExpr* PENewClass::elaborate_expr(Design*des, NetScope*scope,
|
||||||
ivl_type_t ntype, unsigned) const
|
ivl_type_t ntype, unsigned) const
|
||||||
{
|
{
|
||||||
NetENew*tmp = new NetENew(ntype);
|
NetENew*obj = new NetENew(ntype);
|
||||||
tmp->set_line(*this);
|
obj->set_line(*this);
|
||||||
return tmp;
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
43
elab_sig.cc
43
elab_sig.cc
|
|
@ -537,20 +537,41 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const
|
||||||
|
|
||||||
elaborate_sig_wires_(des, scope);
|
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 {
|
} else {
|
||||||
netvector_t*tmp = new netvector_t(IVL_VT_LOGIC);
|
ivl_type_t ret_type;
|
||||||
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);
|
|
||||||
|
|
||||||
ret_sig->set_line(*this);
|
if (return_type_) {
|
||||||
ret_sig->port_type(NetNet::POUTPUT);
|
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;
|
vector<NetNet*>ports;
|
||||||
elaborate_sig_ports_(des, scope, ports);
|
elaborate_sig_ports_(des, scope, ports);
|
||||||
|
|
|
||||||
6
parse.y
6
parse.y
|
|
@ -844,8 +844,10 @@ class_new /* IEEE1800-2005 A.2.4 */
|
||||||
$$ = tmp;
|
$$ = tmp;
|
||||||
}
|
}
|
||||||
| K_new '(' expression_list_proper ')'
|
| K_new '(' expression_list_proper ')'
|
||||||
{ yyerror(@1, "sorry: class_new not implemented yet.");
|
{ PENewClass*tmp = new PENewClass(*$3);
|
||||||
$$ = 0;
|
FILE_NAME(tmp, @1);
|
||||||
|
delete $3;
|
||||||
|
$$ = tmp;
|
||||||
}
|
}
|
||||||
| K_new
|
| K_new
|
||||||
{ PENewClass*tmp = new PENewClass;
|
{ PENewClass*tmp = new PENewClass;
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,15 @@ void PENew::dump(ostream&out) const
|
||||||
|
|
||||||
void PENewClass::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
|
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
|
void PFunction::dump(ostream&out, unsigned ind) const
|
||||||
{
|
{
|
||||||
out << setw(ind) << "" << "function ";
|
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_)
|
if (return_type_)
|
||||||
return_type_->pform_dump(out, ind+8);
|
return_type_->pform_dump(out, ind+8);
|
||||||
else
|
else
|
||||||
out << setw(ind+8) << "" << "<implicit type>" << endl;
|
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_ports_(out, ind);
|
||||||
|
|
||||||
dump_parameters_(out, ind);
|
dump_parameters_(out, ind);
|
||||||
|
|
|
||||||
|
|
@ -88,25 +88,10 @@ static void draw_function_argument(ivl_signal_t port, ivl_expr_t expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void draw_ufunc_preamble(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)
|
|
||||||
{
|
{
|
||||||
unsigned idx;
|
|
||||||
unsigned swid = ivl_expr_width(expr);
|
|
||||||
ivl_scope_t def = ivl_expr_def(expr);
|
ivl_scope_t def = ivl_expr_def(expr);
|
||||||
ivl_signal_t retval = ivl_scope_port(def, 0);
|
unsigned idx;
|
||||||
struct vector_info res;
|
|
||||||
unsigned load_wid;
|
|
||||||
|
|
||||||
/* If this is an automatic function, allocate the local storage. */
|
/* If this is an automatic function, allocate the local storage. */
|
||||||
if (ivl_scope_is_auto(def)) {
|
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));
|
draw_function_argument(port, ivl_expr_parm(expr, idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Call the function */
|
/* Call the function */
|
||||||
fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def)));
|
fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def)));
|
||||||
fprintf(vvp_out, ", S_%p;\n", def);
|
fprintf(vvp_out, ", S_%p;\n", def);
|
||||||
fprintf(vvp_out, " %%join;\n");
|
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. */
|
/* Fresh basic block starts after the join. */
|
||||||
clear_expression_lookaside();
|
clear_expression_lookaside();
|
||||||
|
|
||||||
|
|
@ -159,11 +177,7 @@ struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid)
|
||||||
if (load_wid < wid)
|
if (load_wid < wid)
|
||||||
pad_expr_in_place(expr, res, swid);
|
pad_expr_in_place(expr, res, swid);
|
||||||
|
|
||||||
/* If this is an automatic function, free the local storage. */
|
draw_ufunc_epilogue(expr);
|
||||||
if (ivl_scope_is_auto(def)) {
|
|
||||||
fprintf(vvp_out, " %%free S_%p;\n", def);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,24 +185,9 @@ void draw_ufunc_real(ivl_expr_t expr)
|
||||||
{
|
{
|
||||||
ivl_scope_t def = ivl_expr_def(expr);
|
ivl_scope_t def = ivl_expr_def(expr);
|
||||||
ivl_signal_t retval = ivl_scope_port(def, 0);
|
ivl_signal_t retval = ivl_scope_port(def, 0);
|
||||||
unsigned idx;
|
|
||||||
|
|
||||||
/* If this is an automatic function, allocate the local storage. */
|
/* Take in arguments to function and call the function code. */
|
||||||
if (ivl_scope_is_auto(def)) {
|
draw_ufunc_preamble(expr);
|
||||||
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");
|
|
||||||
|
|
||||||
/* Return value signal cannot be an array. */
|
/* Return value signal cannot be an array. */
|
||||||
assert(ivl_signal_dimensions(retval) == 0);
|
assert(ivl_signal_dimensions(retval) == 0);
|
||||||
|
|
@ -196,9 +195,20 @@ void draw_ufunc_real(ivl_expr_t expr)
|
||||||
/* Load the result into a word. */
|
/* Load the result into a word. */
|
||||||
fprintf(vvp_out, " %%load/real v%p_0;\n", retval);
|
fprintf(vvp_out, " %%load/real v%p_0;\n", retval);
|
||||||
|
|
||||||
/* If this is an automatic function, free the local storage. */
|
draw_ufunc_epilogue(expr);
|
||||||
if (ivl_scope_is_auto(def)) {
|
|
||||||
fprintf(vvp_out, " %%free S_%p;\n", def);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,12 @@ static int eval_object_signal(ivl_expr_t ex)
|
||||||
return 0;
|
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)
|
int draw_eval_object(ivl_expr_t ex)
|
||||||
{
|
{
|
||||||
switch (ivl_expr_type(ex)) {
|
switch (ivl_expr_type(ex)) {
|
||||||
|
|
@ -122,6 +128,9 @@ int draw_eval_object(ivl_expr_t ex)
|
||||||
case IVL_EX_SIGNAL:
|
case IVL_EX_SIGNAL:
|
||||||
return eval_object_signal(ex);
|
return eval_object_signal(ex);
|
||||||
|
|
||||||
|
case IVL_EX_UFUNC:
|
||||||
|
return eval_object_ufunc(ex);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(vvp_out, "; ERROR: draw_eval_object: Invalid expression type %u\n", ivl_expr_type(ex));
|
fprintf(vvp_out, "; ERROR: draw_eval_object: Invalid expression type %u\n", ivl_expr_type(ex));
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -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 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_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,
|
extern void pad_expr_in_place(ivl_expr_t expr, struct vector_info res,
|
||||||
unsigned swid);
|
unsigned swid);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue