Fix type/width for case and case item expressions.

The compiler was treating case and case item expressions as
self-determined. They should be context-sensitive, just like
the operands of a comparison operation.
This commit is contained in:
Martin Whitaker 2013-04-07 12:23:45 +01:00
parent bdfd5b9b55
commit 6da71fdf56
4 changed files with 148 additions and 25 deletions

View File

@ -77,6 +77,23 @@ bool PExpr::is_collapsible_net(Design*, NetScope*) const
return false; return false;
} }
const char* PExpr::width_mode_name(width_mode_t mode)
{
switch (mode) {
case PExpr::SIZED:
return "sized";
case PExpr::EXPAND:
return "expand";
case PExpr::LOSSLESS:
return "lossless";
case PExpr::UNSIZED:
return "unsized";
default:
return "??";
}
}
PEBinary::PEBinary(char op, PExpr*l, PExpr*r) PEBinary::PEBinary(char op, PExpr*l, PExpr*r)
: op_(op), left_(l), right_(r) : op_(op), left_(l), right_(r)
{ {

View File

@ -53,6 +53,9 @@ class PExpr : public LineInfo {
static const unsigned SYS_TASK_ARG = 0x2; static const unsigned SYS_TASK_ARG = 0x2;
static const unsigned ANNOTATABLE = 0x4; static const unsigned ANNOTATABLE = 0x4;
// Convert width mode to human-readable form.
static const char*width_mode_name(width_mode_t mode);
PExpr(); PExpr();
virtual ~PExpr(); virtual ~PExpr();

View File

@ -2803,14 +2803,131 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
return cur; return cur;
} }
static int test_case_width(Design*des, NetScope*scope, PExpr*pe,
PExpr::width_mode_t&mode)
{
unsigned expr_width = pe->test_width(des, scope, mode);
if (debug_elaborate) {
cerr << pe->get_fileline() << ": debug: test_width "
<< "of case expression " << *pe
<< endl;
cerr << pe->get_fileline() << ": "
<< "returns type=" << pe->expr_type()
<< ", width=" << expr_width
<< ", signed=" << pe->has_sign()
<< ", mode=" << PExpr::width_mode_name(mode)
<< endl;
}
return expr_width;
}
static NetExpr*elab_and_eval_case(Design*des, NetScope*scope, PExpr*pe,
bool context_is_real, bool context_unsigned,
unsigned context_width)
{
if (context_unsigned)
pe->cast_signed(false);
unsigned width = context_is_real ? pe->expr_width() : context_width;
NetExpr*expr = pe->elaborate_expr(des, scope, width, PExpr::NO_FLAGS);
if (expr == 0) return 0;
if (context_is_real)
expr = cast_to_real(expr);
eval_expr(expr, context_width);
return expr;
}
/* /*
* Elaborate a case statement. * Elaborate a case statement.
*/ */
NetProc* PCase::elaborate(Design*des, NetScope*scope) const NetProc* PCase::elaborate(Design*des, NetScope*scope) const
{ {
assert(scope); ivl_assert(*this, scope);
NetExpr*expr = elab_and_eval(des, scope, expr_, -1); /* The type of the case expression and case item expressions is
determined according to the following rules:
- if any of the expressions is real, all the expressions are
evaluated as real (non-real expressions will be treated as
self-determined, then converted to real)
- otherwise if any of the expressions is unsigned, all the
expressions are evaluated as unsigned
- otherwise all the expressions are evaluated as signed
If the type is not real, the bit width is determined by the
largest self-determined width of any of the expressions. */
PExpr::width_mode_t context_mode = PExpr::SIZED;
unsigned context_width = test_case_width(des, scope, expr_, context_mode);
bool context_is_real = (expr_->expr_type() == IVL_VT_REAL);
bool context_unsigned = !expr_->has_sign();
for (unsigned idx = 0; idx < items_->count(); idx += 1) {
PCase::Item*cur = (*items_)[idx];
for (list<PExpr*>::iterator idx_expr = cur->expr.begin()
; idx_expr != cur->expr.end() ; ++idx_expr) {
PExpr*cur_expr = *idx_expr;
ivl_assert(*this, cur_expr);
PExpr::width_mode_t cur_mode = PExpr::SIZED;
unsigned cur_width = test_case_width(des, scope, cur_expr,
cur_mode);
if (cur_mode > context_mode)
context_mode = cur_mode;
if (cur_width > context_width)
context_width = cur_width;
if (cur_expr->expr_type() == IVL_VT_REAL)
context_is_real = true;
if (!cur_expr->has_sign())
context_unsigned = true;
}
}
if (context_is_real) {
context_width = 1;
context_unsigned = false;
} else if (context_mode > PExpr::SIZED) {
/* Expressions may choose a different size if they are
in an unsized context, so we need to run through the
process again to get the final expression width. */
context_width = test_case_width(des, scope, expr_, context_mode);
for (unsigned idx = 0; idx < items_->count(); idx += 1) {
PCase::Item*cur = (*items_)[idx];
for (list<PExpr*>::iterator idx_expr = cur->expr.begin()
; idx_expr != cur->expr.end() ; ++idx_expr) {
PExpr*cur_expr = *idx_expr;
ivl_assert(*this, cur_expr);
unsigned cur_width = test_case_width(des, scope, cur_expr,
context_mode);
if (cur_width > context_width)
context_width = cur_width;
}
}
if (context_width < integer_width)
context_width += 1;
}
NetExpr*expr = elab_and_eval_case(des, scope, expr_,
context_is_real,
context_unsigned,
context_width);
if (expr == 0) { if (expr == 0) {
cerr << get_fileline() << ": error: Unable to elaborate this case" cerr << get_fileline() << ": error: Unable to elaborate this case"
" expression." << endl; " expression." << endl;
@ -2840,7 +2957,7 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
unsigned inum = 0; unsigned inum = 0;
for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) {
assert(inum < icount); ivl_assert(*this, inum < icount);
PCase::Item*cur = (*items_)[idx]; PCase::Item*cur = (*items_)[idx];
if (cur->expr.empty()) { if (cur->expr.empty()) {
@ -2861,11 +2978,13 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
a separate case for each. (Yes, the statement a separate case for each. (Yes, the statement
will be elaborated again for each.) */ will be elaborated again for each.) */
PExpr*cur_expr = *idx_expr; PExpr*cur_expr = *idx_expr;
NetExpr*gu; ivl_assert(*this, cur_expr);
NetProc*st = 0; NetExpr*gu = elab_and_eval_case(des, scope, cur_expr,
assert(cur_expr); context_is_real,
gu = elab_and_eval(des, scope, cur_expr, -1); context_unsigned,
context_width);
NetProc*st = 0;
if (cur->stat) if (cur->stat)
st = cur->stat->elaborate(des, scope); st = cur->stat->elaborate(des, scope);

View File

@ -730,22 +730,6 @@ NetExpr* condition_reduce(NetExpr*expr)
return cmp; return cmp;
} }
static const char*width_mode_name(PExpr::width_mode_t mode)
{
switch (mode) {
case PExpr::SIZED:
return "sized";
case PExpr::EXPAND:
return "expand";
case PExpr::LOSSLESS:
return "lossless";
case PExpr::UNSIZED:
return "unsized";
default:
return "??";
}
}
NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
int context_width, bool need_const, bool annotatable, int context_width, bool need_const, bool annotatable,
ivl_variable_type_t cast_type) ivl_variable_type_t cast_type)
@ -773,7 +757,7 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
<< "returns type=" << pe->expr_type() << "returns type=" << pe->expr_type()
<< ", width=" << expr_width << ", width=" << expr_width
<< ", signed=" << pe->has_sign() << ", signed=" << pe->has_sign()
<< ", mode=" << width_mode_name(mode) << endl; << ", mode=" << PExpr::width_mode_name(mode) << endl;
} }
// If we can get the same result using a smaller expression // If we can get the same result using a smaller expression
@ -836,7 +820,7 @@ NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name,
<< "returns type=" << pe->expr_type() << "returns type=" << pe->expr_type()
<< ", width=" << pe->expr_width() << ", width=" << pe->expr_width()
<< ", signed=" << pe->has_sign() << ", signed=" << pe->has_sign()
<< ", mode=" << width_mode_name(mode) << endl; << ", mode=" << PExpr::width_mode_name(mode) << endl;
} }
unsigned flags = PExpr::SYS_TASK_ARG; unsigned flags = PExpr::SYS_TASK_ARG;