Permalink
Browse files

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.
  • Loading branch information...
1 parent 428755c commit 1e9f9685cce3170d2a689ea1bbccc0bde69fd84e @martinwhitaker martinwhitaker committed with Apr 5, 2011
Showing with 184 additions and 31 deletions.
  1. +2 −1 dup_expr.cc
  2. +92 −23 elab_expr.cc
  3. +8 −0 elab_scope.cc
  4. +5 −0 elab_sig.cc
  5. +6 −0 elaborate.cc
  6. +24 −0 eval_tree.cc
  7. +12 −2 net_design.cc
  8. +4 −0 net_scope.cc
  9. +2 −2 netlist.cc
  10. +29 −3 netlist.h
View
@@ -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);
View
@@ -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;
}
View
@@ -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.
View
@@ -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);
View
@@ -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;
}
View
@@ -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;
}
View
@@ -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;
}
View
@@ -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;
View
@@ -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());
}
Oops, something went wrong.

0 comments on commit 1e9f968

Please sign in to comment.