Better handle right shift of unsized expressions.
When doing right shift of unsized expressions, pad the left operand so that the right shift does not lose. This better accounts for the lossless expectations of unsized arguments.
This commit is contained in:
parent
5b9857fe76
commit
8ca3ea2e83
77
elab_expr.cc
77
elab_expr.cc
|
|
@ -589,13 +589,12 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
|
||||||
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
|
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
|
||||||
long shift = rpc->value().as_long();
|
long shift = rpc->value().as_long();
|
||||||
|
|
||||||
// Detect the special cases that the shifted
|
// Special case: The shift is the size of the entire
|
||||||
// unsigned expression is completely shifted away to
|
// left operand, and the shift is unsigned. Elaborate as
|
||||||
// zero.
|
// a constant-0.
|
||||||
if ((op_=='r' || (lp->has_sign()==false))
|
if ((op_=='r' || (lp->has_sign()==false))
|
||||||
&& shift >= (long)lp->expr_width()) {
|
&& shift >= (long)lp->expr_width()) {
|
||||||
// Special case that the value is unsigned
|
|
||||||
// shifted completely away.
|
|
||||||
if (debug_elaborate)
|
if (debug_elaborate)
|
||||||
cerr << get_fileline() << ": debug: "
|
cerr << get_fileline() << ": debug: "
|
||||||
<< "Value right-shifted " << shift
|
<< "Value right-shifted " << shift
|
||||||
|
|
@ -608,8 +607,11 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special case: the shift is the size of the entire
|
||||||
|
// left operand, and the shift is signed. Elaborate as a
|
||||||
|
// replication of the top bit of the left expression.
|
||||||
if (shift >= (long)lp->expr_width()) {
|
if (shift >= (long)lp->expr_width()) {
|
||||||
// Signed right shift.
|
|
||||||
if (debug_elaborate)
|
if (debug_elaborate)
|
||||||
cerr << get_fileline() << ": debug: "
|
cerr << get_fileline() << ": debug: "
|
||||||
<< "Value signed-right-shifted " << shift
|
<< "Value signed-right-shifted " << shift
|
||||||
|
|
@ -625,31 +627,12 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
|
||||||
tmp->cast_signed(true);
|
tmp->cast_signed(true);
|
||||||
tmp = pad_to_width(tmp, use_wid, *this);
|
tmp = pad_to_width(tmp, use_wid, *this);
|
||||||
return tmp;
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (shift >= 0) {
|
// Special case: shift is negative (so do a left shift)
|
||||||
// Signed right shift.
|
// and is greater then the output width. Replace the
|
||||||
if (debug_elaborate)
|
// expression with a constant-0.
|
||||||
cerr << get_fileline() << ": debug: "
|
if (shift < 0 && (0-shift) >= use_wid) {
|
||||||
<< "Value signed-right-shifted " << shift
|
|
||||||
<< " beyond width of " << lp->expr_width()
|
|
||||||
<< "." << endl;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
} else if ((0-shift) >= use_wid) {
|
|
||||||
if (debug_elaborate)
|
if (debug_elaborate)
|
||||||
cerr << get_fileline() << ": debug: "
|
cerr << get_fileline() << ": debug: "
|
||||||
<< "Value signed-right-shifted " << shift
|
<< "Value signed-right-shifted " << shift
|
||||||
|
|
@ -660,6 +643,40 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
|
||||||
tmp->set_line(*this);
|
tmp->set_line(*this);
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ivl_assert(*this, shift >= 0);
|
||||||
|
|
||||||
|
if (debug_elaborate)
|
||||||
|
cerr << get_fileline() << ": debug: "
|
||||||
|
<< "Value signed-right-shifted " << shift
|
||||||
|
<< " beyond width of " << lp->expr_width()
|
||||||
|
<< ". shift=" << shift
|
||||||
|
<< ", expr_wid=" << expr_wid
|
||||||
|
<< ", expr=" << *lp << endl;
|
||||||
|
|
||||||
|
// 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 = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Falback, handle the general case.
|
// Falback, handle the general case.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue