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;
|
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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
3
PExpr.h
3
PExpr.h
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
133
elaborate.cc
133
elaborate.cc
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
20
netmisc.cc
20
netmisc.cc
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue