Improvements to strict-expr-width mode.

Enable error reporting when an unsized number is used in a concatenation
operand. Allow greater pruning of expressions containing unsized numbers.
This commit is contained in:
Martin Whitaker 2013-10-28 22:07:09 +00:00
parent bb39d09d5e
commit 4625e7e2b6
4 changed files with 61 additions and 43 deletions

View File

@ -83,12 +83,14 @@ const char* PExpr::width_mode_name(width_mode_t mode)
switch (mode) { switch (mode) {
case PExpr::SIZED: case PExpr::SIZED:
return "sized"; return "sized";
case PExpr::UNSIZED:
return "unsized";
case PExpr::EXPAND: case PExpr::EXPAND:
return "expand"; return "expand";
case PExpr::LOSSLESS: case PExpr::LOSSLESS:
return "lossless"; return "lossless";
case PExpr::UNSIZED: case PExpr::UPSIZE:
return "unsized"; return "upsize";
default: default:
return "??"; return "??";
} }

42
PExpr.h
View File

@ -45,9 +45,10 @@ class PPackage;
class PExpr : public LineInfo { class PExpr : public LineInfo {
public: public:
enum width_mode_t { SIZED, EXPAND, LOSSLESS, UNSIZED }; // Mode values used by test_width() (see below for description).
enum width_mode_t { SIZED, UNSIZED, EXPAND, LOSSLESS, UPSIZE };
// Flag values that can be passed to elaborate_expr. // Flag values that can be passed to elaborate_expr().
static const unsigned NO_FLAGS = 0x0; static const unsigned NO_FLAGS = 0x0;
static const unsigned NEED_CONST = 0x1; static const unsigned NEED_CONST = 0x1;
static const unsigned SYS_TASK_ARG = 0x2; static const unsigned SYS_TASK_ARG = 0x2;
@ -86,29 +87,38 @@ class PExpr : public LineInfo {
// test the width of an expression. In SIZED mode the expression // test the width of an expression. In SIZED mode the expression
// width will be calculated strictly according to the IEEE standard // width will be calculated strictly according to the IEEE standard
// rules for expression width. // rules for expression width.
// If the expression contains an unsized literal, mode will be //
// changed to LOSSLESS. In LOSSLESS mode the expression width will // If the expression is found to contain an unsized literal number
// be calculated as the minimum width necessary to avoid arithmetic // and gn_strict_expr_width_flag is set, mode will be changed to
// UNSIZED. In UNSIZED mode the expression width will be calculated
// exactly as in SIZED mode - the change in mode simply flags that
// the expression contains an unsized numbers.
//
// If the expression is found to contain an unsized literal number
// and gn_strict_expr_width_flag is not set, mode will be changed
// to LOSSLESS. In LOSSLESS mode the expression width will be
// calculated as the minimum width necessary to avoid arithmetic
// overflow or underflow. // overflow or underflow.
// If the expression both contains an unsized literal and contains //
// Once in LOSSLESS mode, if the expression is found to contain
// an operation that coerces a vector operand to a different type // an operation that coerces a vector operand to a different type
// (signed <-> unsigned), mode is changed to UNSIZED. UNSIZED mode // (signed <-> unsigned), mode will be changed to UPSIZE. UPSIZE
// is the same as LOSSLESS, except that the final expression width // mode is the same as LOSSLESS, except that the final expression
// will be forced to be at least integer_width. This is necessary // width will be forced to be at least integer_width. This is
// to ensure compatibility with the IEEE standard, which requires // necessary to ensure compatibility with the IEEE standard, which
// unsized literals to be treated as having the same width as an // requires unsized numbers to be treated as having the same width
// integer. The lossless width calculation is inadequate in this // as an integer. The lossless width calculation is inadequate in
// case because coercing an operand to a different type means that // this case because coercing an operand to a different type means
// the expression no longer obeys the normal rules of arithmetic. // that the expression no longer obeys the normal rules of arithmetic.
// //
// If mode is initialised to EXPAND instead of SIZED, the expression // If mode is initialised to EXPAND instead of SIZED, the expression
// width will be calculated as the minimum width necessary to avoid // width will be calculated as the minimum width necessary to avoid
// arithmetic overflow or underflow, even if it contains no unsized // arithmetic overflow or underflow, even if it contains no unsized
// literals. mode will be changed LOSSLESS or UNSIZED as described // literals. mode will be changed LOSSLESS or UPSIZE as described
// above. This supports a non-standard mode of expression width // above. This supports a non-standard mode of expression width
// calculation. // calculation.
// //
// When the final value of mode is UNSIZED, the width returned by // When the final value of mode is UPSIZE, the width returned by
// this method is the calculated lossless width, but the width // this method is the calculated lossless width, but the width
// returned by a subsequent call to the expr_width method will be // returned by a subsequent call to the expr_width method will be
// the final expression width. // the final expression width.

View File

@ -136,14 +136,14 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type,
} }
/* /*
* If the mode is UNSIZED, make sure the final expression width is at * If the mode is UPSIZE, make sure the final expression width is at
* least integer_width, but return the calculated lossless width to * least integer_width, but return the calculated lossless width to
* the caller. * the caller.
*/ */
unsigned PExpr::fix_width_(width_mode_t mode) unsigned PExpr::fix_width_(width_mode_t mode)
{ {
unsigned width = expr_width_; unsigned width = expr_width_;
if ((mode == UNSIZED) && type_is_vectorable(expr_type_) if ((mode == UPSIZE) && type_is_vectorable(expr_type_)
&& (width < integer_width)) && (width < integer_width))
expr_width_ = integer_width; expr_width_ = integer_width;
@ -265,7 +265,7 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode)
unsigned r_width = right_->test_width(des, scope, mode); unsigned r_width = right_->test_width(des, scope, mode);
// If the width mode changed, retest the left operand, as it // If the width mode changed, retest the left operand, as it
// may choose a different width if it is in an unsized context. // may choose a different width if it is in a lossless context.
if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS))
l_width = left_->test_width(des, scope, mode); l_width = left_->test_width(des, scope, mode);
@ -293,17 +293,17 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode)
// calculation is unreliable and we need to make sure the // calculation is unreliable and we need to make sure the
// final expression width is at least integer_width. // final expression width is at least integer_width.
if ((mode == LOSSLESS) && (left_->has_sign() != right_->has_sign())) if ((mode == LOSSLESS) && (left_->has_sign() != right_->has_sign()))
mode = UNSIZED; mode = UPSIZE;
switch (op_) { switch (op_) {
case '+': case '+':
case '-': case '-':
if (mode != SIZED) if (mode >= EXPAND)
expr_width_ += 1; expr_width_ += 1;
break; break;
case '*': case '*':
if (mode != SIZED) if (mode >= EXPAND)
expr_width_ = l_width + r_width; expr_width_ = l_width + r_width;
break; break;
@ -575,8 +575,8 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&)
unsigned r_width = right_->test_width(des, scope, mode); unsigned r_width = right_->test_width(des, scope, mode);
// If the width mode changed, retest the left operand, as it // If the width mode changed, retest the left operand, as it
// may choose a different width if it is in an unsized context. // may choose a different width if it is in a lossless context.
if ((mode != SIZED) && (saved_mode == SIZED)) if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS))
l_width = left_->test_width(des, scope, mode); l_width = left_->test_width(des, scope, mode);
ivl_variable_type_t l_type = left_->expr_type(); ivl_variable_type_t l_type = left_->expr_type();
@ -590,14 +590,14 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&)
if (type_is_vectorable(r_type) && (l_width > r_width)) if (type_is_vectorable(r_type) && (l_width > r_width))
r_width_ = l_width; r_width_ = l_width;
// If the expression is unsized and smaller than the integer // If the expression is lossless and smaller than the integer
// minimum, then tweak the size up. // minimum, then tweak the size up.
// NOTE: I really would rather try to figure out what it would // NOTE: I really would rather try to figure out what it would
// take to get expand the sub-expressions so that they are // take to get expand the sub-expressions so that they are
// exactly the right width to behave just like infinite // exactly the right width to behave just like infinite
// width. I suspect that adding 1 more is sufficient in all // width. I suspect that adding 1 more is sufficient in all
// cases, but I'm not certain. Ideas? // cases, but I'm not certain. Ideas?
if (mode != SIZED) { if (mode >= EXPAND) {
if (type_is_vectorable(l_type) && (l_width_ < integer_width)) if (type_is_vectorable(l_type) && (l_width_ < integer_width))
l_width_ += 1; l_width_ += 1;
if (type_is_vectorable(r_type) && (r_width_ < integer_width)) if (type_is_vectorable(r_type) && (r_width_ < integer_width))
@ -725,7 +725,7 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode)
expr_type_ = left_->expr_type(); expr_type_ = left_->expr_type();
signed_flag_ = left_->has_sign(); signed_flag_ = left_->has_sign();
if ((mode != SIZED) && type_is_vectorable(expr_type_)) { if ((mode >= EXPAND) && type_is_vectorable(expr_type_)) {
// We need to make our best guess at the right operand // We need to make our best guess at the right operand
// value, to minimise the calculated width. This is // value, to minimise the calculated width. This is
// particularly important for the power operator... // particularly important for the power operator...
@ -782,7 +782,7 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode)
// shift may do the same, as we don't yet know the final // shift may do the same, as we don't yet know the final
// expression type. // expression type.
if ((mode == LOSSLESS) && signed_flag_) if ((mode == LOSSLESS) && signed_flag_)
mode = UNSIZED; mode = UPSIZE;
break; break;
case 'p': // ** case 'p': // **
@ -1059,7 +1059,7 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope,
min_width_ = expr->min_width(); min_width_ = expr->min_width();
signed_flag_ = (name[1] == 's'); signed_flag_ = (name[1] == 's');
if ((arg_mode != SIZED) && type_is_vectorable(expr_type_)) { if ((arg_mode >= EXPAND) && type_is_vectorable(expr_type_)) {
if (mode < LOSSLESS) if (mode < LOSSLESS)
mode = LOSSLESS; mode = LOSSLESS;
if (expr_width_ < integer_width) if (expr_width_ < integer_width)
@ -2864,8 +2864,8 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
min_width_ = expr_width_; min_width_ = expr_width_;
signed_flag_ = par->has_sign(); signed_flag_ = par->has_sign();
if ((mode < LOSSLESS) && !par->has_width()) if (!par->has_width() && (mode < LOSSLESS))
mode = LOSSLESS; mode = LOSSLESS;
return expr_width_; return expr_width_;
} }
@ -2879,8 +2879,12 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
min_width_ = expr_width_; min_width_ = expr_width_;
signed_flag_ = true; signed_flag_ = true;
if (mode < LOSSLESS) if (gn_strict_expr_width_flag) {
mode = LOSSLESS; expr_width_ = integer_width;
mode = UNSIZED;
} else if (mode < LOSSLESS) {
mode = LOSSLESS;
}
return expr_width_; return expr_width_;
} }
@ -4838,15 +4842,17 @@ unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode)
{ {
expr_type_ = IVL_VT_LOGIC; expr_type_ = IVL_VT_LOGIC;
expr_width_ = value_->len(); expr_width_ = value_->len();
min_width_ = expr_width_;
signed_flag_ = value_->has_sign(); signed_flag_ = value_->has_sign();
if (!value_->has_len() && !value_->is_single()) { if (!value_->has_len() && !value_->is_single()) {
if (gn_strict_expr_width_flag) if (gn_strict_expr_width_flag) {
expr_width_ = integer_width; expr_width_ = integer_width;
else if (mode < LOSSLESS) mode = UNSIZED;
mode = LOSSLESS; } else if (mode < LOSSLESS) {
mode = LOSSLESS;
}
} }
min_width_ = expr_width_;
return expr_width_; return expr_width_;
} }
@ -4941,7 +4947,7 @@ unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode)
unsigned fal_width = fal_->test_width(des, scope, mode); unsigned fal_width = fal_->test_width(des, scope, mode);
// If the width mode changed, retest the true clause, as it // If the width mode changed, retest the true clause, as it
// may choose a different width if it is in an unsized context. // may choose a different width if it is in a lossless context.
if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) { if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) {
tru_width = tru_->test_width(des, scope, mode); tru_width = tru_->test_width(des, scope, mode);
} }
@ -4976,7 +4982,7 @@ unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode)
// calculation is unreliable and we need to make sure the // calculation is unreliable and we need to make sure the
// final expression width is at least integer_width. // final expression width is at least integer_width.
if ((mode == LOSSLESS) && (tru_->has_sign() != fal_->has_sign())) if ((mode == LOSSLESS) && (tru_->has_sign() != fal_->has_sign()))
mode = UNSIZED; mode = UPSIZE;
} }
if (debug_elaborate) if (debug_elaborate)

View File

@ -859,7 +859,7 @@ NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name,
// determine the exact width required to hold the result. // determine the exact width required to hold the result.
// But leave literal numbers exactly as the user supplied // But leave literal numbers exactly as the user supplied
// them. // them.
if ((mode != PExpr::SIZED) && !dynamic_cast<PENumber*>(pe)) if ((mode >= PExpr::LOSSLESS) && !dynamic_cast<PENumber*>(pe))
ce->trim(); ce->trim();
} }