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:
parent
bdfd5b9b55
commit
6da71fdf56
17
PExpr.cc
17
PExpr.cc
|
|
@ -77,6 +77,23 @@ bool PExpr::is_collapsible_net(Design*, NetScope*) const
|
|||
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)
|
||||
: op_(op), left_(l), right_(r)
|
||||
{
|
||||
|
|
|
|||
3
PExpr.h
3
PExpr.h
|
|
@ -53,6 +53,9 @@ class PExpr : public LineInfo {
|
|||
static const unsigned SYS_TASK_ARG = 0x2;
|
||||
static const unsigned ANNOTATABLE = 0x4;
|
||||
|
||||
// Convert width mode to human-readable form.
|
||||
static const char*width_mode_name(width_mode_t mode);
|
||||
|
||||
PExpr();
|
||||
virtual ~PExpr();
|
||||
|
||||
|
|
|
|||
133
elaborate.cc
133
elaborate.cc
|
|
@ -2803,14 +2803,131 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
|
|||
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.
|
||||
*/
|
||||
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) {
|
||||
cerr << get_fileline() << ": error: Unable to elaborate this case"
|
||||
" expression." << endl;
|
||||
|
|
@ -2840,7 +2957,7 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
|
|||
unsigned inum = 0;
|
||||
for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) {
|
||||
|
||||
assert(inum < icount);
|
||||
ivl_assert(*this, inum < icount);
|
||||
PCase::Item*cur = (*items_)[idx];
|
||||
|
||||
if (cur->expr.empty()) {
|
||||
|
|
@ -2861,11 +2978,13 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
|
|||
a separate case for each. (Yes, the statement
|
||||
will be elaborated again for each.) */
|
||||
PExpr*cur_expr = *idx_expr;
|
||||
NetExpr*gu;
|
||||
NetProc*st = 0;
|
||||
assert(cur_expr);
|
||||
gu = elab_and_eval(des, scope, cur_expr, -1);
|
||||
ivl_assert(*this, cur_expr);
|
||||
NetExpr*gu = elab_and_eval_case(des, scope, cur_expr,
|
||||
context_is_real,
|
||||
context_unsigned,
|
||||
context_width);
|
||||
|
||||
NetProc*st = 0;
|
||||
if (cur->stat)
|
||||
st = cur->stat->elaborate(des, scope);
|
||||
|
||||
|
|
|
|||
20
netmisc.cc
20
netmisc.cc
|
|
@ -730,22 +730,6 @@ NetExpr* condition_reduce(NetExpr*expr)
|
|||
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,
|
||||
int context_width, bool need_const, bool annotatable,
|
||||
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()
|
||||
<< ", width=" << expr_width
|
||||
<< ", 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
|
||||
|
|
@ -836,7 +820,7 @@ NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name,
|
|||
<< "returns type=" << pe->expr_type()
|
||||
<< ", width=" << pe->expr_width()
|
||||
<< ", signed=" << pe->has_sign()
|
||||
<< ", mode=" << width_mode_name(mode) << endl;
|
||||
<< ", mode=" << PExpr::width_mode_name(mode) << endl;
|
||||
}
|
||||
|
||||
unsigned flags = PExpr::SYS_TASK_ARG;
|
||||
|
|
|
|||
Loading…
Reference in New Issue