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:
parent
428755ce62
commit
1e9f9685cc
|
|
@ -239,7 +239,8 @@ NetEUFunc* NetEUFunc::dup_expr() const
|
|||
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);
|
||||
tmp->set_line(*this);
|
||||
|
|
|
|||
115
elab_expr.cc
115
elab_expr.cc
|
|
@ -1286,16 +1286,9 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
return elaborate_access_func_(des, scope, access_nature,
|
||||
expr_wid);
|
||||
|
||||
// We do not currently support constant user function and
|
||||
// this is where things fail because of that, though we
|
||||
// don't know for sure so we need to display both messages.
|
||||
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;
|
||||
cerr << get_fileline() << ": error: No function named `" << path_
|
||||
<< "' found in this context (" << scope_path(scope) << ")."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1304,6 +1297,29 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
NetScope*dscope = def->scope();
|
||||
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))
|
||||
return 0;
|
||||
|
||||
|
|
@ -1318,8 +1334,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
of the function being called. The scope of the called
|
||||
function is elaborated when the definition is elaborated. */
|
||||
|
||||
bool need_const = NEED_CONST & flags;
|
||||
|
||||
unsigned parm_errors = 0;
|
||||
unsigned missing_parms = 0;
|
||||
for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) {
|
||||
|
|
@ -1331,8 +1345,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
tmp, need_const);
|
||||
if (parms[idx] == 0) {
|
||||
parm_errors += 1;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (NetEEvent*evt = dynamic_cast<NetEEvent*> (parms[idx])) {
|
||||
cerr << evt->get_fileline() << ": error: An event '"
|
||||
<< evt->event()->name() << "' can not be a user "
|
||||
|
|
@ -1360,6 +1374,26 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
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)
|
||||
return 0;
|
||||
|
||||
|
|
@ -1373,7 +1407,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
|
||||
if (NetNet*res = dscope->find_signal(dscope->basename())) {
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
if ((NEED_CONST & flags) && (path_.size() > 1)) {
|
||||
cerr << get_fileline() << ": error: A hierarchical reference ('"
|
||||
<< path_ << "') is not allowed in a constant expression."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
if (path_.size() > 1) {
|
||||
if (NEED_CONST & flags) {
|
||||
cerr << get_fileline() << ": error: A hierarchical reference"
|
||||
" (`" << path_ << "') is not allowed in a constant"
|
||||
" expression." << endl;
|
||||
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_,
|
||||
|
|
@ -1897,11 +1941,21 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
|||
if (net != 0) {
|
||||
if (NEED_CONST & flags) {
|
||||
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;
|
||||
des->errors += 1;
|
||||
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,
|
||||
expr_wid, flags);
|
||||
|
|
@ -1919,11 +1973,21 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
|||
if (eve != 0) {
|
||||
if (NEED_CONST & flags) {
|
||||
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;
|
||||
des->errors += 1;
|
||||
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);
|
||||
tmp->set_line(*this);
|
||||
|
|
@ -2061,6 +2125,11 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
|||
<< (NEED_CONST & flags ? "parameter" : "wire/reg/memory")
|
||||
<< " `" << path_ << "' in `" << scope_path(scope) << "'"
|
||||
<< 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;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1362,6 +1362,14 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const
|
|||
{
|
||||
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
|
||||
// needed to evaluate the parameter expressions.
|
||||
|
||||
|
|
|
|||
|
|
@ -437,6 +437,11 @@ bool PGenerate::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();
|
||||
assert(scope->type() == NetScope::FUNC);
|
||||
|
||||
|
|
|
|||
|
|
@ -3801,6 +3801,11 @@ NetProc* PForStatement::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();
|
||||
if (def == 0) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
|
|
@ -3816,6 +3821,7 @@ void PFunction::elaborate(Design*des, NetScope*scope) const
|
|||
if (st == 0) {
|
||||
cerr << statement_->get_fileline() << ": error: Unable to elaborate "
|
||||
"statement in function " << scope->basename() << "." << endl;
|
||||
scope->is_const_func(true); // error recovery
|
||||
des->errors += 1;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
24
eval_tree.cc
24
eval_tree.cc
|
|
@ -1946,5 +1946,29 @@ NetExpr* NetESFunc::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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
# include "compiler.h"
|
||||
# include "netmisc.h"
|
||||
# include "PExpr.h"
|
||||
# include "PTask.h"
|
||||
# include <sstream>
|
||||
# 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);
|
||||
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 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
|
|||
{
|
||||
events_ = 0;
|
||||
lcounter_ = 0;
|
||||
need_const_func_ = false;
|
||||
is_const_func_ = false;
|
||||
is_auto_ = 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 */
|
||||
break;
|
||||
}
|
||||
func_pform_ = 0;
|
||||
elab_stage_ = 1;
|
||||
lineno_ = 0;
|
||||
def_lineno_ = 0;
|
||||
genvar_tmp_val = 0;
|
||||
|
|
|
|||
|
|
@ -1988,8 +1988,8 @@ const NetExpr* NetSTask::parm(unsigned idx) const
|
|||
}
|
||||
|
||||
NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res,
|
||||
svector<NetExpr*>&p)
|
||||
: scope_(scope), func_(def), result_sig_(res), parms_(p)
|
||||
svector<NetExpr*>&p, bool nc)
|
||||
: scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc)
|
||||
{
|
||||
expr_width(result_sig_->expr_width());
|
||||
}
|
||||
|
|
|
|||
32
netlist.h
32
netlist.h
|
|
@ -73,6 +73,7 @@ class NetTaskDef;
|
|||
class NetEvTrig;
|
||||
class NetEvWait;
|
||||
class PExpr;
|
||||
class PFunction;
|
||||
class netenum_t;
|
||||
|
||||
struct target;
|
||||
|
|
@ -790,6 +791,29 @@ class NetScope : public Attrib {
|
|||
unsigned get_def_lineno() const { return def_lineno_; };
|
||||
|
||||
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. */
|
||||
void is_auto(bool is_auto__) { is_auto_ = 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);
|
||||
|
||||
|
||||
struct spec_val_t {
|
||||
ivl_variable_type_t type;
|
||||
union {
|
||||
|
|
@ -954,6 +977,8 @@ class NetScope : public Attrib {
|
|||
NetTaskDef*task_;
|
||||
NetFuncDef*func_;
|
||||
};
|
||||
const PFunction*func_pform_;
|
||||
unsigned elab_stage_;
|
||||
|
||||
// Enumerations. The enum_sets_ is a list of all the
|
||||
// enumerations present in this scope. The enum_names_ is a
|
||||
|
|
@ -966,7 +991,7 @@ class NetScope : public Attrib {
|
|||
map<hname_t,NetScope*> children_;
|
||||
|
||||
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 {
|
||||
|
||||
public:
|
||||
NetEUFunc(NetScope*, NetScope*, NetESignal*, svector<NetExpr*>&);
|
||||
NetEUFunc(NetScope*, NetScope*, NetESignal*, svector<NetExpr*>&, bool);
|
||||
~NetEUFunc();
|
||||
|
||||
const NetESignal*result_sig() const;
|
||||
|
|
@ -3119,6 +3144,7 @@ class NetEUFunc : public NetExpr {
|
|||
NetScope*func_;
|
||||
NetESignal*result_sig_;
|
||||
svector<NetExpr*> parms_;
|
||||
bool need_const_;
|
||||
|
||||
private: // not implemented
|
||||
NetEUFunc(const NetEUFunc&);
|
||||
|
|
|
|||
Loading…
Reference in New Issue