Checks for illegal use of automatically allocated variables.

This patch adds a number of compile and run-time checks for illegal
uses of variables declared in automatic tasks and functions. It
also adds a check for event expressions in automatic tasks that use
features not yet supported in VVP.
This commit is contained in:
Martin Whitaker 2008-11-09 00:26:55 +00:00 committed by Stephen Williams
parent bbdf622ea5
commit 04377151bc
15 changed files with 283 additions and 43 deletions

View File

@ -24,6 +24,7 @@
# include "compiler.h"
# include "PExpr.h"
# include "Module.h"
# include "netmisc.h"
# include <typeinfo>
PExpr::PExpr()
@ -35,6 +36,11 @@ PExpr::~PExpr()
{
}
bool PExpr::has_aa_term(Design*, NetScope*) const
{
return false;
}
bool PExpr::is_the_same(const PExpr*that) const
{
return typeid(this) == typeid(that);
@ -64,6 +70,12 @@ PEBinary::~PEBinary()
{
}
bool PEBinary::has_aa_term(Design*des, NetScope*scope) const
{
assert(left_ && right_);
return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope);
}
PEBComp::PEBComp(char op, PExpr*l, PExpr*r)
: PEBinary(op, l, r)
{
@ -130,6 +142,15 @@ PECallFunction::~PECallFunction()
{
}
bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const
{
bool flag = false;
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
flag = parms_[idx]->has_aa_term(des, scope) || flag;
}
return flag;
}
PEConcat::PEConcat(const svector<PExpr*>&p, PExpr*r)
: parms_(p), repeat_(r)
{
@ -140,6 +161,18 @@ PEConcat::~PEConcat()
delete repeat_;
}
bool PEConcat::has_aa_term(Design*des, NetScope*scope) const
{
bool flag = false;
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
flag = parms_[idx]->has_aa_term(des, scope) || flag;
}
if (repeat_)
flag = repeat_->has_aa_term(des, scope) || flag;
return flag;
}
PEEvent::PEEvent(PEEvent::edge_t t, PExpr*e)
: type_(t), expr_(e)
{
@ -154,6 +187,12 @@ PEEvent::edge_t PEEvent::type() const
return type_;
}
bool PEEvent::has_aa_term(Design*des, NetScope*scope) const
{
assert(expr_);
return expr_->has_aa_term(des, scope);
}
PExpr* PEEvent::expr() const
{
return expr_;
@ -188,6 +227,22 @@ PEIdent::~PEIdent()
{
}
bool PEIdent::has_aa_term(Design*des, NetScope*scope) const
{
NetNet* net = 0;
const NetExpr*par = 0;
NetEvent* eve = 0;
const NetExpr*ex1, *ex2;
scope = symbol_search(0, des, scope, path_, net, par, eve, ex1, ex2);
if (scope)
return scope->is_auto();
else
return false;
}
PENumber::PENumber(verinum*vp)
: value_(vp)
{
@ -237,6 +292,14 @@ PETernary::~PETernary()
{
}
bool PETernary::has_aa_term(Design*des, NetScope*scope) const
{
assert(expr_ && tru_ && fal_);
return expr_->has_aa_term(des, scope)
|| tru_->has_aa_term(des, scope)
|| fal_->has_aa_term(des, scope);
}
PEUnary::PEUnary(char op, PExpr*ex)
: op_(op), expr_(ex)
{
@ -245,3 +308,9 @@ PEUnary::PEUnary(char op, PExpr*ex)
PEUnary::~PEUnary()
{
}
bool PEUnary::has_aa_term(Design*des, NetScope*scope) const
{
assert(expr_);
return expr_->has_aa_term(des, scope);
}

22
PExpr.h
View File

@ -46,6 +46,10 @@ class PExpr : public LineInfo {
virtual void dump(ostream&) const;
// This method tests whether the expression contains any
// references to automatically allocated variables.
virtual bool has_aa_term(Design*des, NetScope*scope) const;
// This method tests the width that the expression wants to
// be. It is used by elaboration of assignments to figure out
// the width of the expression.
@ -156,6 +160,8 @@ class PEConcat : public PExpr {
virtual verinum* eval_const(Design*des, NetScope*sc) const;
virtual void dump(ostream&) const;
virtual bool has_aa_term(Design*des, NetScope*scope) const;
virtual unsigned test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval,
ivl_variable_type_t&expr_type,
@ -200,6 +206,8 @@ class PEEvent : public PExpr {
virtual void dump(ostream&) const;
virtual bool has_aa_term(Design*des, NetScope*scope) const;
private:
edge_t type_;
PExpr *expr_;
@ -247,6 +255,9 @@ class PEIdent : public PExpr {
void append_name(perm_string);
virtual void dump(ostream&) const;
virtual bool has_aa_term(Design*des, NetScope*scope) const;
virtual unsigned test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval,
ivl_variable_type_t&expr_type,
@ -420,6 +431,8 @@ class PEUnary : public PExpr {
virtual void dump(ostream&out) const;
virtual bool has_aa_term(Design*des, NetScope*scope) const;
virtual unsigned test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval,
ivl_variable_type_t&expr_type,
@ -448,6 +461,8 @@ class PEBinary : public PExpr {
virtual void dump(ostream&out) const;
virtual bool has_aa_term(Design*des, NetScope*scope) const;
virtual unsigned test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval,
ivl_variable_type_t&expr_type,
@ -541,6 +556,9 @@ class PETernary : public PExpr {
~PETernary();
virtual void dump(ostream&out) const;
virtual bool has_aa_term(Design*des, NetScope*scope) const;
virtual unsigned test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval,
ivl_variable_type_t&expr_type,
@ -579,7 +597,9 @@ class PECallFunction : public PExpr {
virtual void dump(ostream &) const;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
virtual bool has_aa_term(Design*des, NetScope*scope) const;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
int expr_wid, bool sys_task_arg) const;
virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const;

View File

@ -228,6 +228,15 @@ void PEventStatement::set_statement(Statement*st)
statement_ = st;
}
bool PEventStatement::has_aa_term(Design*des, NetScope*scope)
{
bool flag = false;
for (unsigned idx = 0 ; idx < expr_.count() ; idx += 1) {
flag = expr_[idx]->has_aa_term(des, scope) || flag;
}
return flag;
}
PForce::PForce(PExpr*l, PExpr*r)
: lval_(l), expr_(r)
{

View File

@ -347,6 +347,8 @@ class PEventStatement : public Statement {
virtual void elaborate_scope(Design*des, NetScope*scope) const;
virtual void elaborate_sig(Design*des, NetScope*scope) const;
bool has_aa_term(Design*des, NetScope*scope);
// This method is used to elaborate, but attach a previously
// elaborated statement to the event.
NetProc* elaborate_st(Design*des, NetScope*scope, NetProc*st) const;

View File

@ -1123,8 +1123,9 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const
? NetScope::FORK_JOIN
: NetScope::BEGIN_END);
my_scope->set_line(get_file(), get_lineno());
my_scope->is_auto(scope->is_auto());
// Scan the parameters in the module, and create stub parameter
// Scan the parameters in the scope, and create stub parameter
// entries in the scope for the parameter names.
collect_scope_parameters_(my_scope, parameters);

View File

@ -2012,6 +2012,14 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
return 0;
}
if (scope->is_auto() && lval()->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be assigned values using non-blocking "
"assignments." << endl;
des->errors += 1;
return 0;
}
/* Elaborate the l-value. */
NetAssign_*lv = elaborate_lval(des, scope);
if (lv == 0) return 0;
@ -2042,6 +2050,15 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
NetEvWait*event = 0;
if (count_ != 0 || event_ != 0) {
if (count_ != 0) {
if (scope->is_auto() && count_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically "
"allocated variables may not be referenced "
"in intra-assignment event controls of "
"non-blocking assignments." << endl;
des->errors += 1;
return 0;
}
assert(event_ != 0);
count = elab_and_eval(des, scope, count_, -1);
if (count == 0) {
@ -2052,6 +2069,15 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
}
}
if (scope->is_auto() && event_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically "
"allocated variables may not be referenced "
"in intra-assignment event controls of "
"non-blocking assignments." << endl;
des->errors += 1;
return 0;
}
NetProc*st = event_->elaborate(des, scope);
if (st == 0) {
cerr << get_fileline() << ": unable to elaborate "
@ -2586,6 +2612,22 @@ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const
NetCAssign*dev = 0;
assert(scope);
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be assigned values using procedural "
"continuous assignments." << endl;
des->errors += 1;
return 0;
}
if (scope->is_auto() && expr_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be referenced in procedural "
"continuous assignments." << endl;
des->errors += 1;
return 0;
}
NetAssign_*lval = lval_->elaborate_lval(des, scope, false);
if (lval == 0)
return 0;
@ -2617,6 +2659,14 @@ NetDeassign* PDeassign::elaborate(Design*des, NetScope*scope) const
{
assert(scope);
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be assigned values using procedural "
"continuous assignments." << endl;
des->errors += 1;
return 0;
}
NetAssign_*lval = lval_->elaborate_lval(des, scope, false);
if (lval == 0)
return 0;
@ -2875,6 +2925,16 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
the sub-expression as a net and decide how to handle
the edge. */
if (scope->is_auto()) {
if (! dynamic_cast<PEIdent*>(expr_[idx]->expr())) {
cerr << get_fileline() << ": sorry, complex event "
"expressions are not yet supported in "
"automatic tasks." << endl;
des->errors += 1;
return 0;
}
}
bool save_flag = error_implicit;
error_implicit = true;
NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), 0);
@ -3150,6 +3210,22 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const
NetForce*dev = 0;
assert(scope);
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be assigned values using procedural "
"force statements." << endl;
des->errors += 1;
return 0;
}
if (scope->is_auto() && expr_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be referenced in procedural force "
"statements." << endl;
des->errors += 1;
return 0;
}
NetAssign_*lval = lval_->elaborate_lval(des, scope, true);
if (lval == 0)
return 0;
@ -3355,6 +3431,14 @@ NetProc* PRelease::elaborate(Design*des, NetScope*scope) const
{
assert(scope);
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
cerr << get_fileline() << ": error: automatically allocated "
"variables may not be assigned values using procedural "
"force statements." << endl;
des->errors += 1;
return 0;
}
NetAssign_*lval = lval_->elaborate_lval(des, scope, true);
if (lval == 0)
return 0;

View File

@ -100,7 +100,8 @@ static int vpi_get_dec_size(vpiHandle item)
);
}
static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv)
static int array_from_iterator(struct strobe_cb_info*info, vpiHandle argv,
int check_no_aa_vars)
{
if (argv) {
vpiHandle item;
@ -111,13 +112,17 @@ static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv)
free(items);
info->nitems = 0;
info->items = 0;
return;
return check_no_aa_vars;
}
if (check_no_aa_vars && vpi_get(vpiAutomatic, items[0]))
check_no_aa_vars = 0;
for (item = vpi_scan(argv) ; item ; item = vpi_scan(argv)) {
items = realloc(items, (nitems+1)*sizeof(vpiHandle));
items[nitems] = item;
nitems += 1;
if (check_no_aa_vars && vpi_get(vpiAutomatic, item))
check_no_aa_vars = 0;
}
info->nitems = nitems;
@ -127,6 +132,7 @@ static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv)
info->nitems = 0;
info->items = 0;
}
return check_no_aa_vars;
}
/*
@ -902,7 +908,7 @@ static PLI_INT32 sys_display_calltf(PLI_BYTE8*name)
info = malloc(sizeof (struct strobe_cb_info));
info->default_format = get_default_format(name);
info->scope = scope;
array_from_iterator(info, argv);
array_from_iterator(info, argv, 0);
vpi_put_userdata(sys, info);
}
@ -995,7 +1001,13 @@ static PLI_INT32 sys_strobe_calltf(PLI_BYTE8*name)
info->mcd = 1;
}
array_from_iterator(info, argv);
if (!array_from_iterator(info, argv, 1)) {
vpi_printf("ERROR: %s parameters may not include automatically "
"allocated variables\n", name);
free(info->items);
free(info);
return 0;
}
info->name = strdup(name);
info->default_format = get_default_format(name);
info->scope= scope;
@ -1095,7 +1107,13 @@ static PLI_INT32 sys_monitor_calltf(PLI_BYTE8*name)
}
/* Make an array of handles from the argument list. */
array_from_iterator(&monitor_info, argv);
if (!array_from_iterator(&monitor_info, argv, 1)) {
vpi_printf("ERROR: $monitor parameters may not include "
"automatically allocated variables\n");
free(monitor_info.items);
monitor_info.nitems = 0;
return 0;
}
monitor_info.name = strdup(name);
monitor_info.default_format = get_default_format(name);
monitor_info.scope = scope;
@ -1211,7 +1229,7 @@ static PLI_INT32 sys_fdisplay_calltf(PLI_BYTE8*name)
assert(scope);
info.default_format = get_default_format(name);
info.scope = scope;
array_from_iterator(&info, argv);
array_from_iterator(&info, argv, 0);
do_display(mcd, &info);
free(info.items);
@ -1969,7 +1987,7 @@ static PLI_INT32 sys_swrite_calltf(PLI_BYTE8 *name)
info.name = name;
info.default_format = get_default_format(name);
info.scope = scope;
array_from_iterator(&info, argv);
array_from_iterator(&info, argv, 0);
/* Because %u and %z may put embedded NULL characters into the returned
* string strlen() may not match the real size! */
@ -2049,7 +2067,7 @@ static PLI_INT32 sys_sformat_calltf(PLI_BYTE8 *name)
info.name = name;
info.default_format = get_default_format(name);
info.scope = scope;
array_from_iterator(&info, argv);
array_from_iterator(&info, argv, 0);
idx = -1;
size = get_format(&result, fmt, &info, &idx);
free(fmt);

View File

@ -454,6 +454,9 @@ static int vpi_array_var_word_get(int code, vpiHandle ref)
case vpiRightRange:
return parent->lsb.value;
case vpiAutomatic:
return (int) parent->scope->is_automatic;
default:
return 0;
}
@ -634,6 +637,9 @@ static int vpi_array_vthr_A_get(int code, vpiHandle ref)
case vpiRightRange:
return parent->lsb.value;
case vpiAutomatic:
return (int) parent->scope->is_automatic;
// For now &A<> is only a constant select. This will need
// to be changed when it supports variable selection.
case vpiConstantSelect:

View File

@ -119,6 +119,14 @@ void delete_vpi_callback(struct __vpiCallback* ref)
*/
static struct __vpiCallback* make_value_change(p_cb_data data)
{
if (vpi_get(vpiAutomatic, data->obj)) {
fprintf(stderr, "vpi error: cannot place value change "
"callback on automatically allocated "
"variable '%s'\n",
vpi_get_str(vpiName, data->obj));
return 0;
}
struct __vpiCallback*obj = new_vpi_callback();
obj->cb_data = *data;
if (data->time) {

View File

@ -47,6 +47,9 @@ static int string_get(int code, vpiHandle ref)
case vpiConstType:
return vpiStringConst;
case vpiAutomatic:
return 0;
default:
fprintf(stderr, "vvp error: get %d not supported "
"by vpiStringConst\n", code);
@ -343,6 +346,9 @@ static int binary_get(int code, vpiHandle ref)
case vpiSize:
return rfp->bits.size();
case vpiAutomatic:
return 0;
default:
fprintf(stderr, "vvp error: get %d not supported "
"by vpiBinaryConst\n", code);
@ -533,6 +539,9 @@ static int dec_get(int code, vpiHandle ref)
case vpiSize:
return 32;
case vpiAutomatic:
return 0;
default:
fprintf(stderr, "vvp error: get %d not supported "
"by vpiDecConst\n", code);
@ -636,6 +645,9 @@ static int real_get(int code, vpiHandle ref)
case vpiSigned:
return 1;
case vpiAutomatic:
return 0;
default:
fprintf(stderr, "vvp error: get %d not supported "
"by vpiDecConst\n", code);

View File

@ -692,6 +692,14 @@ vpiHandle vpi_put_value(vpiHandle obj, s_vpi_value*vp,
if (flags!=vpiNoDelay && flags!=vpiForceFlag && flags!=vpiReleaseFlag) {
vvp_time64_t dly;
if (vpi_get(vpiAutomatic, obj)) {
fprintf(stderr, "vpi error: cannot put a value with "
"a delay on automatically allocated "
"variable '%s'\n",
vpi_get_str(vpiName, obj));
return 0;
}
assert(when != 0);
switch (when->type) {

View File

@ -223,7 +223,6 @@ struct __vpiSignal {
unsigned signed_flag : 1;
unsigned isint_ : 1; // original type was integer
unsigned is_netarray : 1; // This is word of a net array
unsigned is_automatic : 1;
/* The represented value is here. */
vvp_net_t*node;
};

View File

@ -75,6 +75,9 @@ static int scope_get(int code, vpiHandle obj)
case vpiTopModule:
return 0x0 == ref->scope;
case vpiAutomatic:
return (int) ref->is_automatic;
}
return 0;
@ -330,33 +333,29 @@ compile_scope_decl(char*label, char*type, char*name, const char*tname,
struct __vpiScope*scope = new struct __vpiScope;
count_vpi_scopes += 1;
if (strcmp(type,"module") == 0) {
char*base_type = 0;
if (strncmp(type,"auto",4) == 0) {
scope->is_automatic = true;
base_type = &type[4];
} else {
scope->is_automatic = false;
base_type = &type[0];
}
if (strcmp(base_type,"module") == 0) {
scope->base.vpi_type = &vpip_scope_module_rt;
scope->is_automatic = false;
} else if (strcmp(type,"autofunction") == 0) {
} else if (strcmp(base_type,"function") == 0) {
scope->base.vpi_type = &vpip_scope_function_rt;
scope->is_automatic = true;
} else if (strcmp(type,"function") == 0) {
scope->base.vpi_type = &vpip_scope_function_rt;
scope->is_automatic = false;
} else if (strcmp(type,"autotask") == 0) {
} else if (strcmp(base_type,"task") == 0) {
scope->base.vpi_type = &vpip_scope_task_rt;
scope->is_automatic = true;
} else if (strcmp(type,"task") == 0) {
scope->base.vpi_type = &vpip_scope_task_rt;
scope->is_automatic = false;
} else if (strcmp(type,"fork") == 0) {
} else if (strcmp(base_type,"fork") == 0) {
scope->base.vpi_type = &vpip_scope_fork_rt;
scope->is_automatic = false;
} else if (strcmp(type,"begin") == 0) {
} else if (strcmp(base_type,"begin") == 0) {
scope->base.vpi_type = &vpip_scope_begin_rt;
scope->is_automatic = false;
} else if (strcmp(type,"generate") == 0) {
} else if (strcmp(base_type,"generate") == 0) {
scope->base.vpi_type = &vpip_scope_begin_rt;
scope->is_automatic = false;
} else {
scope->base.vpi_type = &vpip_scope_module_rt;
scope->is_automatic = false;
assert(0);
}
@ -396,10 +395,6 @@ compile_scope_decl(char*label, char*type, char*name, const char*tname,
scope->time_units = sp->time_units;
scope->time_precision = sp->time_precision;
/* Scopes within automatic scopes are themselves automatic. */
if (sp->is_automatic)
scope->is_automatic = true;
} else {
scope->scope = 0x0;
@ -476,5 +471,3 @@ unsigned vpip_add_item_to_context(automatic_hooks_s*item,
/* Offset the context index by 2 to leave space for the list links. */
return 2 + idx;
}

View File

@ -528,10 +528,14 @@ static int signal_get(int code, vpiHandle ref)
else
return 0;
case vpiLeftRange: return rfp->msb;
case vpiRightRange: return rfp->lsb;
case vpiLeftRange:
return rfp->msb;
case vpiAutomatic: return rfp->is_automatic;
case vpiRightRange:
return rfp->lsb;
case vpiAutomatic:
return (int) vpip_scope(rfp)->is_automatic;
case _vpiNexusId:
if (rfp->msb == rfp->lsb)
@ -863,7 +867,6 @@ vpiHandle vpip_make_int(const char*name, int msb, int lsb, vvp_net_t*vec)
struct __vpiSignal*rfp = (struct __vpiSignal*)obj;
obj->vpi_type = &vpip_reg_rt;
rfp->isint_ = true;
rfp->is_automatic = vpip_peek_current_scope()->is_automatic;
return obj;
}
@ -876,7 +879,6 @@ vpiHandle vpip_make_reg(const char*name, int msb, int lsb,
vpiHandle obj = vpip_make_net(name, msb,lsb, signed_flag, vec);
struct __vpiSignal*rfp = (struct __vpiSignal*)obj;
obj->vpi_type = &vpip_reg_rt;
rfp->is_automatic = vpip_peek_current_scope()->is_automatic;
return obj;
}
@ -915,7 +917,6 @@ vpiHandle vpip_make_net(const char*name, int msb, int lsb,
obj->signed_flag = signed_flag? 1 : 0;
obj->isint_ = 0;
obj->is_netarray = 0;
obj->is_automatic = vpip_peek_current_scope()->is_automatic;
obj->node = node;
// Place this object within a scope. If this object is
@ -972,11 +973,15 @@ static int PV_get(int code, vpiHandle ref)
case vpiConstantSelect:
return rfp->twid == 0;
case vpiLeftRange: rval += rfp->width;
case vpiLeftRange:
rval += rfp->width;
case vpiRightRange:
rval += vpi_get(vpiRightRange, rfp->parent) + PV_get_base(rfp);
return rval;
case vpiAutomatic:
return vpi_get(vpiAutomatic, rfp->parent);
default:
fprintf(stderr, "PV_get: property %d is unknown\n", code);
}

View File

@ -93,6 +93,9 @@ static int timevar_time_get(int code, vpiHandle ref)
case vpiFuncType:
return vpiTimeFunc;
case vpiAutomatic:
return 0;
default:
fprintf(stderr, "Code: %d\n", code);
assert(0);
@ -148,6 +151,9 @@ static int timevar_realtime_get(int code, vpiHandle ref)
case vpiFuncType:
return vpiRealFunc;
case vpiAutomatic:
return 0;
default:
fprintf(stderr, "Code: %d\n", code);
assert(0);