Miscellaneous improvements and fixes to shift elaboration (v0.9).

This patch ensures that the result of a shift is an undefined value
if the right operand is an undefined value. It also improves the
code generated for right shifts where the right operand is constant
(which fixes the compiler crash on pr3098439b in the test suite).
This commit is contained in:
Martin Whitaker 2011-03-12 20:26:54 +00:00 committed by Stephen Williams
parent 337eb3f8e7
commit 9084c4aab1
2 changed files with 66 additions and 40 deletions

View File

@ -509,9 +509,11 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des,
if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) { if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) {
// Special case: The left expression is zero. No matter // Special case: The left expression is zero. If the
// what the shift, the result is going to be zero. // shift value contains no 'x' or 'z' bits, the result
if (lpc->value().is_defined() && lpc->value().is_zero()) { // is going to be zero.
if (lpc->value().is_defined() && lpc->value().is_zero()
&& (rp->expr_type() == IVL_VT_BOOL)) {
if (debug_elaborate) if (debug_elaborate)
cerr << get_fileline() << ": debug: " cerr << get_fileline() << ": debug: "
@ -527,6 +529,11 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des,
// Handle the super-special case that both // Handle the super-special case that both
// operands are constants. Precalculate the // operands are constants. Precalculate the
// entire value here. // entire value here.
if (!rpc->value().is_defined()) {
tmp = make_const_x(use_wid);
tmp->set_line(*this);
return tmp;
}
verinum lpval = lpc->value(); verinum lpval = lpc->value();
unsigned shift = rpc->value().as_ulong(); unsigned shift = rpc->value().as_ulong();
verinum result = lpc->value() << shift; verinum result = lpc->value() << shift;
@ -566,6 +573,20 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des,
} }
} else if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) { } else if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
// Special case: The shift value contains 'x' or 'z' bits.
// Elaborate as a constant-x.
if (!rpc->value().is_defined()) {
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Shift by undefined value. "
<< "Elaborate as constant 'x'." << endl;
tmp = make_const_x(expr_wid);
tmp->set_line(*this);
return tmp;
}
long shift = rpc->value().as_long(); long shift = rpc->value().as_long();
use_wid = lp->expr_width(); use_wid = lp->expr_width();
if (expr_wid > 0) if (expr_wid > 0)
@ -594,6 +615,11 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des,
// default way. // default way.
if (expr_wid >= 0) if (expr_wid >= 0)
lp = pad_to_width(lp, expr_wid, *this); lp = pad_to_width(lp, expr_wid, *this);
rp = new NetESelect(rp, 0, rp->expr_width());
rp->cast_signed(false);
rp->set_line(*this);
tmp = new NetEBShift(op_, lp, rp); tmp = new NetEBShift(op_, lp, rp);
} }
@ -631,9 +657,11 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) { if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) {
// Special case: The left expression is zero. No matter // Special case: The left expression is zero. If the
// what the shift, the result is going to be zero. // shift value contains no 'x' or 'z' bits, the result
if (lpc->value().is_defined() && lpc->value().is_zero()) { // is going to be zero.
if (lpc->value().is_defined() && lpc->value().is_zero()
&& (rp->expr_type() == IVL_VT_BOOL)) {
if (debug_elaborate) if (debug_elaborate)
cerr << get_fileline() << ": debug: " cerr << get_fileline() << ": debug: "
@ -647,6 +675,20 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
} }
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) { if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
// Special case: The shift value contains 'x' or 'z' bits.
// Elaborate as a constant-x.
if (!rpc->value().is_defined()) {
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Shift by undefined value. "
<< "Elaborate as constant 'x'." << endl;
tmp = make_const_x(expr_wid);
tmp->set_line(*this);
return tmp;
}
unsigned long shift = rpc->value().as_ulong(); unsigned long shift = rpc->value().as_ulong();
// Special case: The shift is the size of the entire // Special case: The shift is the size of the entire
@ -692,36 +734,16 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
tmp = pad_to_width(tmp, use_wid, *this); tmp = pad_to_width(tmp, use_wid, *this);
return tmp; return tmp;
} }
// If this is lossless, then pad the left expression
// enough to cover the right shift.
if (expr_wid == -2 && use_wid+shift > lp->expr_width()) {
lp->cast_signed(lp->has_sign() && op_=='R');
lp = pad_to_width(lp, use_wid + shift, *this);
}
tmp = new NetEConst(verinum(shift));
tmp->set_line(*this);
long tmp_wid = lp->expr_width() - shift;
if (tmp_wid > use_wid)
tmp_wid = use_wid;
ivl_assert(*this, tmp_wid > 0);
ivl_assert(*this, use_wid > 0);
// Implement the right-shift by part-selecting the low
// bits out. Pad the result of the part select back out
// to the desired size.
tmp = new NetESelect(lp, tmp, tmp_wid);
tmp->set_line(*this);
tmp->cast_signed(lp->has_sign() && op_=='R');
tmp = pad_to_width(tmp, use_wid, *this);
return tmp;
} }
// Fallback, handle the general case. // Fallback, handle the general case.
if (expr_wid > 0) if (expr_wid > 0)
lp = pad_to_width(lp, expr_wid, *this); lp = pad_to_width(lp, expr_wid, *this);
rp = new NetESelect(rp, 0, rp->expr_width());
rp->cast_signed(false);
rp->set_line(*this);
tmp = new NetEBShift(op_, lp, rp); tmp = new NetEBShift(op_, lp, rp);
tmp->set_line(*this); tmp->set_line(*this);
return tmp; return tmp;

View File

@ -1157,8 +1157,9 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid)
case 'l': /* << (left shift) */ case 'l': /* << (left shift) */
lv = draw_eval_expr_wid(le, wid, 0); lv = draw_eval_expr_wid(le, wid, 0);
/* shifting 0 gets 0. */ /* Shifting 0 gets 0, if we can be sure the shift value
if (lv.base == 0) contains no 'x' or 'z' bits. */
if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL))
return lv; return lv;
if (lv.base < 4) { if (lv.base < 4) {
@ -1193,8 +1194,9 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid)
lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0); lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0);
} }
/* shifting 0 gets 0. */ /* Shifting 0 gets 0, if we can be sure the shift value
if (lv.base == 0) contains no 'x' or 'z' bits. */
if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL))
return lv; return lv;
if (lv.base < 4) { if (lv.base < 4) {
@ -1229,13 +1231,15 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid)
lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0); lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0);
} }
/* shifting 0 gets 0. */ /* Shifting 0 gets 0, if we can be sure the shift value
if (lv.base == 0) contains no 'x' or 'z' bits. */
if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL))
return lv; return lv;
/* Sign extend any constant begets itself, if this /* Similarly, sign extending any constant bit begets itself,
expression is signed. */ if this expression is signed. */
if ((lv.base < 4) && (ivl_expr_signed(expr))) if ((lv.base < 4) && ivl_expr_signed(expr)
&& (ivl_expr_value(re) == IVL_VT_BOOL))
return lv; return lv;
if (lv.base < 4) { if (lv.base < 4) {