First step towards supporting constant user functions.

This patch allows the compiler to perform early elaboration
of functions if they are encountered in expressions that are
elaborated before the function would normally be elaborated.
This makes the function available for constant evaluation.
Suitable error messages are generated if a function that is
used in a constant expression is not a valid constant function.
This commit is contained in:
Martin Whitaker 2011-04-05 20:43:54 +01:00 committed by Stephen Williams
parent 428755ce62
commit 1e9f9685cc
10 changed files with 184 additions and 31 deletions

View File

@ -239,7 +239,8 @@ NetEUFunc* NetEUFunc::dup_expr() const
tmp_parms[idx] = parms_[idx]->dup_expr(); tmp_parms[idx] = parms_[idx]->dup_expr();
} }
tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms); tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms,
need_const_);
ivl_assert(*this, tmp); ivl_assert(*this, tmp);
tmp->set_line(*this); tmp->set_line(*this);

View File

@ -1286,16 +1286,9 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
return elaborate_access_func_(des, scope, access_nature, return elaborate_access_func_(des, scope, access_nature,
expr_wid); expr_wid);
// We do not currently support constant user function and cerr << get_fileline() << ": error: No function named `" << path_
// this is where things fail because of that, though we << "' found in this context (" << scope_path(scope) << ")."
// don't know for sure so we need to display both messages. << endl;
if (NEED_CONST & flags) {
cerr << get_fileline() << ": sorry: constant user "
"functions are not currently supported: "
<< path_ << "()." << endl << " or" << endl;
}
cerr << get_fileline() << ": error: No function " << path_
<< " in this context (" << scope_path(scope) << ")." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
} }
@ -1304,6 +1297,29 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
NetScope*dscope = def->scope(); NetScope*dscope = def->scope();
ivl_assert(*this, dscope); ivl_assert(*this, dscope);
bool need_const = NEED_CONST & flags;
// It is possible to get here before the called function has been
// fully elaborated. If this is the case, elaborate it now. This
// ensures we know whether or not it is a constant function.
if (dscope->elab_stage() < 3) {
dscope->need_const_func(need_const);
const PFunction*pfunc = dscope->func_pform();
ivl_assert(*this, pfunc);
pfunc->elaborate(des, dscope);
}
if (dscope->parent() != scope->parent() || !dscope->is_const_func()) {
if (scope->need_const_func()) {
cerr << get_fileline() << ": error: A function invoked by "
"a constant function must be a constant function "
"local to the current module." << endl;
des->errors += 1;
return 0;
}
scope->is_const_func(false);
}
if (! check_call_matches_definition_(des, dscope)) if (! check_call_matches_definition_(des, dscope))
return 0; return 0;
@ -1318,8 +1334,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
of the function being called. The scope of the called of the function being called. The scope of the called
function is elaborated when the definition is elaborated. */ function is elaborated when the definition is elaborated. */
bool need_const = NEED_CONST & flags;
unsigned parm_errors = 0; unsigned parm_errors = 0;
unsigned missing_parms = 0; unsigned missing_parms = 0;
for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) { for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) {
@ -1331,8 +1345,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
tmp, need_const); tmp, need_const);
if (parms[idx] == 0) { if (parms[idx] == 0) {
parm_errors += 1; parm_errors += 1;
continue; continue;
} }
if (NetEEvent*evt = dynamic_cast<NetEEvent*> (parms[idx])) { if (NetEEvent*evt = dynamic_cast<NetEEvent*> (parms[idx])) {
cerr << evt->get_fileline() << ": error: An event '" cerr << evt->get_fileline() << ": error: An event '"
<< evt->event()->name() << "' can not be a user " << evt->event()->name() << "' can not be a user "
@ -1360,6 +1374,26 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
des->errors += 1; des->errors += 1;
} }
if (need_const && !dscope->is_const_func()) {
// If this is the first time the function has been called in
// a constant context, force the function to be re-elaborated.
// This will generate the necessary error messages to allow
// the user to diagnose the fault.
if (!dscope->need_const_func()) {
dscope->set_elab_stage(2);
dscope->need_const_func(true);
const PFunction*pfunc = dscope->func_pform();
ivl_assert(*this, pfunc);
pfunc->elaborate(des, dscope);
}
cerr << get_fileline() << ": error: `" << dscope->basename()
<< "' is not a constant function." << endl;
des->errors += 1;
return 0;
}
if (missing_parms || parm_errors) if (missing_parms || parm_errors)
return 0; return 0;
@ -1373,7 +1407,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
if (NetNet*res = dscope->find_signal(dscope->basename())) { if (NetNet*res = dscope->find_signal(dscope->basename())) {
NetESignal*eres = new NetESignal(res); NetESignal*eres = new NetESignal(res);
NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms); NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms, need_const);
func->set_line(*this); func->set_line(*this);
NetExpr*tmp = pad_to_width(func, expr_wid, *this); NetExpr*tmp = pad_to_width(func, expr_wid, *this);
@ -1866,12 +1900,22 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
const NetExpr*ex1, *ex2; const NetExpr*ex1, *ex2;
if ((NEED_CONST & flags) && (path_.size() > 1)) { if (path_.size() > 1) {
cerr << get_fileline() << ": error: A hierarchical reference ('" if (NEED_CONST & flags) {
<< path_ << "') is not allowed in a constant expression." cerr << get_fileline() << ": error: A hierarchical reference"
<< endl; " (`" << path_ << "') is not allowed in a constant"
des->errors += 1; " expression." << endl;
return 0; des->errors += 1;
return 0;
}
if (scope->need_const_func()) {
cerr << get_fileline() << ": error: A hierarchical reference"
" (`" << path_ << "') is not allowed in a constant"
" function." << endl;
des->errors += 1;
return 0;
}
scope->is_const_func(false);
} }
NetScope*found_in = symbol_search(this, des, scope, path_, NetScope*found_in = symbol_search(this, des, scope, path_,
@ -1897,11 +1941,21 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
if (net != 0) { if (net != 0) {
if (NEED_CONST & flags) { if (NEED_CONST & flags) {
cerr << get_fileline() << ": error: A reference to a wire " cerr << get_fileline() << ": error: A reference to a wire "
"or register ('" << path_ << "') is not allowed in " "or reg (`" << path_ << "') is not allowed in "
"a constant expression." << endl; "a constant expression." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
} }
if (net->scope() != scope) {
if (scope->need_const_func()) {
cerr << get_fileline() << ": error: A reference to a "
"non-local wire or reg (`" << path_ << "') is "
"not allowed in a constant function." << endl;
des->errors += 1;
return 0;
}
scope->is_const_func(false);
}
NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in,
expr_wid, flags); expr_wid, flags);
@ -1919,11 +1973,21 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
if (eve != 0) { if (eve != 0) {
if (NEED_CONST & flags) { if (NEED_CONST & flags) {
cerr << get_fileline() << ": error: A reference to a named " cerr << get_fileline() << ": error: A reference to a named "
"event ('" << path_ << "') is not allowed in a " "event (`" << path_ << "') is not allowed in a "
"constant expression." << endl; "constant expression." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
} }
if (eve->scope() != scope) {
if (scope->need_const_func()) {
cerr << get_fileline() << ": error: A reference to a "
"non-local named event (`" << path_ << "') is "
"not allowed in a constant function." << endl;
des->errors += 1;
return 0;
}
scope->is_const_func(false);
}
NetEEvent*tmp = new NetEEvent(eve); NetEEvent*tmp = new NetEEvent(eve);
tmp->set_line(*this); tmp->set_line(*this);
@ -2061,6 +2125,11 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
<< (NEED_CONST & flags ? "parameter" : "wire/reg/memory") << (NEED_CONST & flags ? "parameter" : "wire/reg/memory")
<< " `" << path_ << "' in `" << scope_path(scope) << "'" << " `" << path_ << "' in `" << scope_path(scope) << "'"
<< endl; << endl;
if (scope->need_const_func()) {
cerr << get_fileline() << ": : `" << scope->basename()
<< "' is being used as a constant function, so may "
"only reference local variables." << endl;
}
des->errors += 1; des->errors += 1;
return 0; return 0;
} }

View File

@ -1362,6 +1362,14 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const
{ {
assert(scope->type() == NetScope::FUNC); assert(scope->type() == NetScope::FUNC);
// Save a reference to the pform representation of the function
// in case we need to perform early elaboration.
scope->set_func_pform(this);
// Assume the function is a constant function until we
// find otherwise.
scope->is_const_func(true);
// Scan the parameters in the function, and store the information // Scan the parameters in the function, and store the information
// needed to evaluate the parameter expressions. // needed to evaluate the parameter expressions.

View File

@ -437,6 +437,11 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const
*/ */
void PFunction::elaborate_sig(Design*des, NetScope*scope) const void PFunction::elaborate_sig(Design*des, NetScope*scope) const
{ {
if (scope->elab_stage() > 1)
return;
scope->set_elab_stage(2);
perm_string fname = scope->basename(); perm_string fname = scope->basename();
assert(scope->type() == NetScope::FUNC); assert(scope->type() == NetScope::FUNC);

View File

@ -3801,6 +3801,11 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
void PFunction::elaborate(Design*des, NetScope*scope) const void PFunction::elaborate(Design*des, NetScope*scope) const
{ {
if (scope->elab_stage() > 2)
return;
scope->set_elab_stage(3);
NetFuncDef*def = scope->func_def(); NetFuncDef*def = scope->func_def();
if (def == 0) { if (def == 0) {
cerr << get_fileline() << ": internal error: " cerr << get_fileline() << ": internal error: "
@ -3816,6 +3821,7 @@ void PFunction::elaborate(Design*des, NetScope*scope) const
if (st == 0) { if (st == 0) {
cerr << statement_->get_fileline() << ": error: Unable to elaborate " cerr << statement_->get_fileline() << ": error: Unable to elaborate "
"statement in function " << scope->basename() << "." << endl; "statement in function " << scope->basename() << "." << endl;
scope->is_const_func(true); // error recovery
des->errors += 1; des->errors += 1;
return; return;
} }

View File

@ -1946,5 +1946,29 @@ NetExpr* NetESFunc::eval_tree()
NetExpr* NetEUFunc::eval_tree() NetExpr* NetEUFunc::eval_tree()
{ {
// If we know the function cannot be evaluated as a constant,
// give up now.
if (!func()->is_const_func())
return 0;
// Variables inside static functions can be accessed from outside
// the function, so we can't be sure they are constant unless the
// function was called in a constant context.
if (!func()->is_auto() && !need_const_)
return 0;
// Run through the input parameters to check they are constants.
for (unsigned idx = 0; idx < parm_count(); idx += 1) {
if (dynamic_cast<const NetEConst*> (parm(idx)))
continue;
if (dynamic_cast<const NetECReal*> (parm(idx)))
continue;
return 0;
}
if (need_const_) {
cerr << get_fileline() << ": sorry: Constant user functions are "
"not yet supported." << endl;
}
return 0; return 0;
} }

View File

@ -33,6 +33,7 @@
# include "compiler.h" # include "compiler.h"
# include "netmisc.h" # include "netmisc.h"
# include "PExpr.h" # include "PExpr.h"
# include "PTask.h"
# include <sstream> # include <sstream>
# include "ivl_assert.h" # include "ivl_assert.h"
@ -720,9 +721,18 @@ NetFuncDef* Design::find_function(NetScope*scope, const pform_name_t&name)
std::list<hname_t> eval_path = eval_scope_path(this, scope, name); std::list<hname_t> eval_path = eval_scope_path(this, scope, name);
NetScope*func = find_scope(scope, eval_path, NetScope::FUNC); NetScope*func = find_scope(scope, eval_path, NetScope::FUNC);
if (func && (func->type() == NetScope::FUNC)) if (func && (func->type() == NetScope::FUNC)) {
// If a function is used in a parameter definition or in
// a signal declaration, it is possible to get here before
// the function's signals have been elaborated. If this is
// the case, elaborate them now.
if (func->elab_stage() < 2) {
const PFunction*pfunc = func->func_pform();
assert(pfunc);
pfunc->elaborate_sig(this, func);
}
return func->func_def(); return func->func_def();
}
return 0; return 0;
} }

View File

@ -43,6 +43,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
{ {
events_ = 0; events_ = 0;
lcounter_ = 0; lcounter_ = 0;
need_const_func_ = false;
is_const_func_ = false;
is_auto_ = false; is_auto_ = false;
is_cell_ = false; is_cell_ = false;
@ -72,6 +74,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
default: /* BEGIN_END and FORK_JOIN, do nothing */ default: /* BEGIN_END and FORK_JOIN, do nothing */
break; break;
} }
func_pform_ = 0;
elab_stage_ = 1;
lineno_ = 0; lineno_ = 0;
def_lineno_ = 0; def_lineno_ = 0;
genvar_tmp_val = 0; genvar_tmp_val = 0;

View File

@ -1988,8 +1988,8 @@ const NetExpr* NetSTask::parm(unsigned idx) const
} }
NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res,
svector<NetExpr*>&p) svector<NetExpr*>&p, bool nc)
: scope_(scope), func_(def), result_sig_(res), parms_(p) : scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc)
{ {
expr_width(result_sig_->expr_width()); expr_width(result_sig_->expr_width());
} }

View File

@ -73,6 +73,7 @@ class NetTaskDef;
class NetEvTrig; class NetEvTrig;
class NetEvWait; class NetEvWait;
class PExpr; class PExpr;
class PFunction;
class netenum_t; class netenum_t;
struct target; struct target;
@ -790,6 +791,29 @@ class NetScope : public Attrib {
unsigned get_def_lineno() const { return def_lineno_; }; unsigned get_def_lineno() const { return def_lineno_; };
bool in_func() const; bool in_func() const;
/* Provide a link back to the pform to allow early elaboration of
constant functions. */
void set_func_pform(const PFunction*pfunc) { func_pform_ = pfunc; };
const PFunction*func_pform() const { return func_pform_; };
/* Allow tracking of elaboration stages. The three stages are:
1 - scope elaboration
2 - signal elaboration
3 - statement elaboration
This is only used for functions, to support early elaboration.
*/
void set_elab_stage(unsigned stage) { elab_stage_ = stage; };
unsigned elab_stage() const { return elab_stage_; };
/* Is this a function called in a constant expression. */
void need_const_func(bool need_const) { need_const_func_ = need_const; };
bool need_const_func() const { return need_const_func_; };
/* Is this a constant function. */
void is_const_func(bool is_const) { is_const_func_ = is_const; };
bool is_const_func() const { return is_const_func_; };
/* Is the task or function automatic. */ /* Is the task or function automatic. */
void is_auto(bool is_auto__) { is_auto_ = is_auto__; }; void is_auto(bool is_auto__) { is_auto_ = is_auto__; };
bool is_auto() const { return is_auto_; }; bool is_auto() const { return is_auto_; };
@ -904,7 +928,6 @@ class NetScope : public Attrib {
param_ref_t find_parameter(perm_string name); param_ref_t find_parameter(perm_string name);
struct spec_val_t { struct spec_val_t {
ivl_variable_type_t type; ivl_variable_type_t type;
union { union {
@ -954,6 +977,8 @@ class NetScope : public Attrib {
NetTaskDef*task_; NetTaskDef*task_;
NetFuncDef*func_; NetFuncDef*func_;
}; };
const PFunction*func_pform_;
unsigned elab_stage_;
// Enumerations. The enum_sets_ is a list of all the // Enumerations. The enum_sets_ is a list of all the
// enumerations present in this scope. The enum_names_ is a // enumerations present in this scope. The enum_names_ is a
@ -966,7 +991,7 @@ class NetScope : public Attrib {
map<hname_t,NetScope*> children_; map<hname_t,NetScope*> children_;
unsigned lcounter_; unsigned lcounter_;
bool is_auto_, is_cell_; bool need_const_func_, is_const_func_, is_auto_, is_cell_;
}; };
/* /*
@ -3095,7 +3120,7 @@ class NetTaskDef {
class NetEUFunc : public NetExpr { class NetEUFunc : public NetExpr {
public: public:
NetEUFunc(NetScope*, NetScope*, NetESignal*, svector<NetExpr*>&); NetEUFunc(NetScope*, NetScope*, NetESignal*, svector<NetExpr*>&, bool);
~NetEUFunc(); ~NetEUFunc();
const NetESignal*result_sig() const; const NetESignal*result_sig() const;
@ -3119,6 +3144,7 @@ class NetEUFunc : public NetExpr {
NetScope*func_; NetScope*func_;
NetESignal*result_sig_; NetESignal*result_sig_;
svector<NetExpr*> parms_; svector<NetExpr*> parms_;
bool need_const_;
private: // not implemented private: // not implemented
NetEUFunc(const NetEUFunc&); NetEUFunc(const NetEUFunc&);