Don't evaluate built-in system functions if they are overridden.

We don't support evaluating user-defined system functions at compile
time. If possible, defer evaluation until run time. If used in a
constant expression, output a "sorry" message.
This commit is contained in:
Martin Whitaker 2019-10-19 16:12:17 +01:00
parent bf655003e8
commit 1069a0ef02
9 changed files with 72 additions and 32 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2016 Stephen Williams <steve@icarus.com>
* Copyright (c) 1998-2019 Stephen Williams <steve@icarus.com>
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -203,12 +203,12 @@ PEBShift::~PEBShift()
}
PECallFunction::PECallFunction(const pform_name_t&n, const vector<PExpr *> &parms)
: package_(0), path_(n), parms_(parms)
: package_(0), path_(n), parms_(parms), is_overridden_(false)
{
}
PECallFunction::PECallFunction(PPackage*pkg, const pform_name_t&n, const vector<PExpr *> &parms)
: package_(pkg), path_(n), parms_(parms)
: package_(pkg), path_(n), parms_(parms), is_overridden_(false)
{
}
@ -221,7 +221,7 @@ static pform_name_t pn_from_ps(perm_string n)
}
PECallFunction::PECallFunction(PPackage*pkg, perm_string n, const list<PExpr *> &parms)
: package_(pkg), path_(pn_from_ps(n)), parms_(parms.size())
: package_(pkg), path_(pn_from_ps(n)), parms_(parms.size()), is_overridden_(false)
{
int tmp_idx = 0;
assert(parms_.size() == parms.size());
@ -231,18 +231,18 @@ PECallFunction::PECallFunction(PPackage*pkg, perm_string n, const list<PExpr *>
}
PECallFunction::PECallFunction(perm_string n, const vector<PExpr*>&parms)
: package_(0), path_(pn_from_ps(n)), parms_(parms)
: package_(0), path_(pn_from_ps(n)), parms_(parms), is_overridden_(false)
{
}
PECallFunction::PECallFunction(perm_string n)
: package_(0), path_(pn_from_ps(n))
: package_(0), path_(pn_from_ps(n)), is_overridden_(false)
{
}
// NOTE: Anachronism. Try to work all use of svector out.
PECallFunction::PECallFunction(const pform_name_t&n, const list<PExpr *> &parms)
: package_(0), path_(n), parms_(parms.size())
: package_(0), path_(n), parms_(parms.size()), is_overridden_(false)
{
int tmp_idx = 0;
assert(parms_.size() == parms.size());
@ -252,7 +252,7 @@ PECallFunction::PECallFunction(const pform_name_t&n, const list<PExpr *> &parms)
}
PECallFunction::PECallFunction(perm_string n, const list<PExpr*>&parms)
: package_(0), path_(pn_from_ps(n)), parms_(parms.size())
: package_(0), path_(pn_from_ps(n)), parms_(parms.size()), is_overridden_(false)
{
int tmp_idx = 0;
assert(parms_.size() == parms.size());

View File

@ -928,6 +928,9 @@ class PECallFunction : public PExpr {
pform_name_t path_;
std::vector<PExpr *> parms_;
// For system functions.
bool is_overridden_;
bool check_call_matches_definition_(Design*des, NetScope*dscope) const;

View File

@ -286,7 +286,8 @@ struct sfunc_return_type {
const char* name;
ivl_variable_type_t type;
unsigned wid;
int signed_flag;
bool signed_flag;
bool override_flag;
};
extern const struct sfunc_return_type* lookup_sys_func(const char*name);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2019 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -233,7 +233,7 @@ NetESelect* NetESelect::dup_expr() const
NetESFunc* NetESFunc::dup_expr() const
{
NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms());
NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms(), is_overridden_);
ivl_assert(*this, tmp);
tmp->cast_signed(has_sign());

View File

@ -1202,6 +1202,8 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope,
min_width_ = expr_width_;
signed_flag_ = sfunc_info->signed_flag;
is_overridden_ = sfunc_info->override_flag;
if (debug_elaborate)
cerr << get_fileline() << ": debug: test_width "
<< "of system function " << name
@ -1547,10 +1549,19 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope,
if ((nparms == 1) && (parms_[0] == 0))
nparms = 0;
NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms);
NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms, is_overridden_);
fun->set_line(*this);
if (!fun->is_built_in()) {
bool need_const = NEED_CONST & flags;
/* We don't support evaluating overridden functions. */
if (is_overridden_ && (need_const || scope->need_const_func())) {
cerr << get_fileline() << ": sorry: Cannot evaluate "
"overridden system function." << endl;
des->errors += 1;
}
if (is_overridden_ || !fun->is_built_in()) {
if (scope->need_const_func()) {
cerr << get_fileline() << ": error: " << name
<< " is not a built-in function, so cannot"
@ -1567,8 +1578,6 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope,
expression as much as possible, and use the reduced
expression if one is created. */
bool need_const = NEED_CONST & flags;
/* These functions can work in a constant context with a signal expression. */
if ((nparms == 1) && (dynamic_cast<PEIdent*>(parms_[0]))) {
if (strcmp(name, "$dimensions") == 0) need_const = false;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2018 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2019 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -2400,6 +2400,10 @@ NetESFunc::ID NetESFunc::built_in_id_() const
NetExpr* NetESFunc::eval_tree()
{
/* We don't support evaluating overridden functions. */
if (is_overridden_)
return 0;
/* Get the ID for this system function if it can be used as a
* constant function. */
ID id = built_in_id_();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 2002-2019 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -461,15 +461,15 @@ bool NetESelect::has_width() const
}
NetESFunc::NetESFunc(const char*n, ivl_variable_type_t t,
unsigned width, unsigned np)
: name_(0), type_(t), enum_type_(0), parms_(np)
unsigned width, unsigned np, bool is_overridden)
: name_(0), type_(t), enum_type_(0), parms_(np), is_overridden_(is_overridden)
{
name_ = lex_strings.add(n);
expr_width(width);
}
NetESFunc::NetESFunc(const char*n, ivl_type_t rtype, unsigned np)
: NetExpr(rtype), name_(0), type_(IVL_VT_NO_TYPE), enum_type_(0), parms_(np)
: NetExpr(rtype), name_(0), type_(IVL_VT_NO_TYPE), enum_type_(0), parms_(np), is_overridden_(false)
{
name_ = lex_strings.add(n);
expr_width(rtype->packed_width());
@ -485,7 +485,7 @@ NetESFunc::NetESFunc(const char*n, ivl_type_t rtype, unsigned np)
}
NetESFunc::NetESFunc(const char*n, const netenum_t*enum_type, unsigned np)
: name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np)
: name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np), is_overridden_(false)
{
name_ = lex_strings.add(n);
expr_width(enum_type->packed_width());

View File

@ -4538,7 +4538,7 @@ class NetESFunc : public NetExpr {
public:
NetESFunc(const char*name, ivl_variable_type_t t,
unsigned width, unsigned nprms);
unsigned width, unsigned nprms, bool is_overridden =false);
NetESFunc(const char*name, ivl_type_t rtype, unsigned nprms);
NetESFunc(const char*name, const netenum_t*enum_type, unsigned nprms);
~NetESFunc();
@ -4634,6 +4634,7 @@ class NetESFunc : public NetExpr {
ivl_variable_type_t type_;
const netenum_t*enum_type_;
std::vector<NetExpr*>parms_;
bool is_overridden_;
ID built_in_id_() const;

View File

@ -30,14 +30,14 @@
*/
static const struct sfunc_return_type sfunc_table[] = {
{ "$realtime", IVL_VT_REAL, 1, 0 },
{ "$bitstoreal", IVL_VT_REAL, 1, 0 },
{ "$itor", IVL_VT_REAL, 1, 0 },
{ "$realtobits", IVL_VT_LOGIC, 64, 0 },
{ "$time", IVL_VT_LOGIC, 64, 0 },
{ "$stime", IVL_VT_LOGIC, 32, 0 },
{ "$simtime", IVL_VT_LOGIC, 64, 0 },
{ 0, IVL_VT_LOGIC, 32, 0 }
{ "$realtime", IVL_VT_REAL, 1, false, false },
{ "$bitstoreal", IVL_VT_REAL, 1, false, false },
{ "$itor", IVL_VT_REAL, 1, false, false },
{ "$realtobits", IVL_VT_LOGIC, 64, false, false },
{ "$time", IVL_VT_LOGIC, 64, false, false },
{ "$stime", IVL_VT_LOGIC, 32, false, false },
{ "$simtime", IVL_VT_LOGIC, 64, false, false },
{ 0, IVL_VT_LOGIC, 32, false, false }
};
struct sfunc_return_type_cell : sfunc_return_type {
@ -69,9 +69,8 @@ void cleanup_sys_func_table()
}
}
const struct sfunc_return_type* lookup_sys_func(const char*name)
static struct sfunc_return_type* find_in_sys_func_list(const char*name)
{
/* First, try to find the name in the function list. */
struct sfunc_return_type_cell*cur = sfunc_list_head;
while (cur) {
if (strcmp(cur->name, name) == 0)
@ -80,6 +79,16 @@ const struct sfunc_return_type* lookup_sys_func(const char*name)
cur = cur->next;
}
return 0;
}
const struct sfunc_return_type* lookup_sys_func(const char*name)
{
/* First, try to find the name in the function list. */
struct sfunc_return_type*def = find_in_sys_func_list(name);
if (def)
return def;
/* Next, look in the core table. */
unsigned idx = 0;
while (sfunc_table[idx].name) {
@ -147,12 +156,21 @@ int load_sys_func_table(const char*path)
cp = stype + strcspn(stype, " \t\r\n");
if (cp[0]) *cp++ = 0;
struct sfunc_return_type*def = find_in_sys_func_list(name);
if (def) {
/* Keep the original definition, but flag that it
overrides a later definition. */
def->override_flag = true;
continue;
}
if (strcmp(stype,"vpiSysFuncReal") == 0) {
cell = new struct sfunc_return_type_cell;
cell->name = lex_strings.add(name);
cell->type = IVL_VT_REAL;
cell->wid = 1;
cell->signed_flag = true;
cell->override_flag = false;
append_to_list(cell);
continue;
}
@ -163,6 +181,7 @@ int load_sys_func_table(const char*path)
cell->type = IVL_VT_LOGIC;
cell->wid = 32;
cell->signed_flag = true;
cell->override_flag = false;
append_to_list(cell);
continue;
}
@ -203,6 +222,7 @@ int load_sys_func_table(const char*path)
cell->type = IVL_VT_LOGIC;
cell->wid = width;
cell->signed_flag = signed_flag;
cell->override_flag = false;
append_to_list(cell);
continue;
}
@ -213,6 +233,7 @@ int load_sys_func_table(const char*path)
cell->type = IVL_VT_VOID;
cell->wid = 0;
cell->signed_flag = false;
cell->override_flag = false;
append_to_list(cell);
continue;
}
@ -223,6 +244,7 @@ int load_sys_func_table(const char*path)
cell->type = IVL_VT_STRING;
cell->wid = 0; // string is a dynamic length type
cell->signed_flag = false;
cell->override_flag = false;
append_to_list(cell);
continue;
}