Process shift by constant amounts early in expression elaboration.

The expr:::synthesize methods need not deal with saturating left or
right shifts if they are dealt with early, in elaborate_expr methods.
So the elaborate_expr for shift takes on much more responsibility.
This commit is contained in:
Stephen Williams 2008-08-20 21:47:07 -07:00
parent 0de2dcb211
commit e18eb32d8b
6 changed files with 225 additions and 77 deletions

View File

@ -579,6 +579,9 @@ class PEBinary : public PExpr {
NetExpr*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const;
NetExpr*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const;
NetExpr*elaborate_expr_base_lshift_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const;
NetExpr*elaborate_expr_base_rshift_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const;
static void suppress_operand_sign_if_needed_(NetExpr*lp, NetExpr*rp);
private:

View File

@ -101,6 +101,7 @@ class PAssign_ : public Statement {
protected:
NetAssign_* elaborate_lval(Design*, NetScope*scope) const;
NetExpr* elaborate_rval_(Design*, NetScope*, unsigned lv_width) const;
PExpr* delay_;
PEventStatement*event_;

View File

@ -209,64 +209,12 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des,
break;
case 'l': // <<
if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) {
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
// Handle the super-special case that both
// operands are constants. Precalculate the
// entire value here.
verinum lpval = lpc->value();
unsigned shift = rpc->value().as_ulong();
verinum result = lpc->value() << shift;
// If the l-value has explicit size, or
// there is a context determined size, use that.
if (lpval.has_len() || expr_wid > 0) {
int use_len = lpval.len();
if (expr_wid < use_len)
use_len = expr_wid;
result = verinum(result, lpval.len());
}
tmp = new NetEConst(result);
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Precalculate " << *this
<< " to constant " << *tmp << endl;
} else {
// Handle the special case that the left
// operand is constant. If it is unsized, we
// may have to expand it to an integer width.
verinum lpval = lpc->value();
if (lpval.len() < integer_width && !lpval.has_len()) {
lpval = verinum(lpval, integer_width);
lpc = new NetEConst(lpval);
lpc->set_line(*lp);
}
tmp = new NetEBShift(op_, lpc, rp);
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Adjust " << *this
<< " to this " << *tmp
<< " to allow for integer widths." << endl;
}
} else {
// Left side is not constant, so handle it the
// default way.
if (expr_wid >= 0)
lp = pad_to_width(lp, expr_wid);
tmp = new NetEBShift(op_, lp, rp);
}
tmp->set_line(*this);
tmp = elaborate_expr_base_lshift_(des, lp, rp, expr_wid);
break;
case 'r': // >>
case 'R': // >>>
if (expr_wid > 0)
lp = pad_to_width(lp, expr_wid);
tmp = new NetEBShift(op_, lp, rp);
tmp->set_line(*this);
tmp = elaborate_expr_base_rshift_(des, lp, rp, expr_wid);
break;
case '^':
@ -333,6 +281,190 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des,
return tmp;
}
NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des,
NetExpr*lp, NetExpr*rp,
int expr_wid) const
{
NetExpr*tmp;
if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) {
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
// Handle the super-special case that both
// operands are constants. Precalculate the
// entire value here.
verinum lpval = lpc->value();
unsigned shift = rpc->value().as_ulong();
verinum result = lpc->value() << shift;
// If the l-value has explicit size, or
// there is a context determined size, use that.
if (lpval.has_len() || expr_wid > 0) {
int use_len = lpval.len();
if (expr_wid < use_len)
use_len = expr_wid;
result = verinum(result, lpval.len());
}
tmp = new NetEConst(result);
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Precalculate " << *this
<< " to constant " << *tmp << endl;
} else {
// Handle the special case that the left
// operand is constant. If it is unsized, we
// may have to expand it to an integer width.
verinum lpval = lpc->value();
if (lpval.len() < integer_width && !lpval.has_len()) {
lpval = verinum(lpval, integer_width);
lpc = new NetEConst(lpval);
lpc->set_line(*lp);
}
tmp = new NetEBShift(op_, lpc, rp);
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Adjust " << *this
<< " to this " << *tmp
<< " to allow for integer widths." << endl;
}
} else if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
long shift = rpc->value().as_long();
long use_wid = lp->expr_width();
if (expr_wid > 0)
use_wid = expr_wid;
if (shift >= use_wid || (-shift) >= (long)lp->expr_width()) {
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Value left-shifted " << shift
<< " beyond width of " << use_wid
<< ". Elaborate as constant zero." << endl;
tmp = make_const_0(use_wid);
} else {
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Left shift expression by constant "
<< shift << " bits. (use_wid=" << use_wid << ")" << endl;
lp = pad_to_width(lp, use_wid);
tmp = new NetEBShift(op_, lp, rp);
}
} else {
// Left side is not constant, so handle it the
// default way.
if (expr_wid >= 0)
lp = pad_to_width(lp, expr_wid);
tmp = new NetEBShift(op_, lp, rp);
}
tmp->set_line(*this);
return tmp;
}
NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
NetExpr*lp, NetExpr*rp,
int expr_wid) const
{
NetExpr*tmp;
long use_wid = lp->expr_width();
if (expr_wid > 0)
use_wid = expr_wid;
if (use_wid == 0) {
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Oops, left expression width is not known, "
<< "so expression width is not known. Punt." << endl;
tmp = new NetEBShift(op_, lp, rp);
tmp->set_line(*this);
return tmp;
}
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
long shift = rpc->value().as_long();
// Detect the special cases that the shifted
// unsigned expression is completely shifted away to
// zero.
if ((op_=='r' || (lp->has_sign()==false))
&& shift >= (long)lp->expr_width()) {
// Special case that the value is unsigned
// shifted completely away.
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Value right-shifted " << shift
<< " beyond width of " << lp->expr_width()
<< ". Elaborate as constant zero." << endl;
tmp = make_const_0(use_wid);
tmp->set_line(*this);
return tmp;
}
if (shift >= (long)lp->expr_width()) {
// Signed right shift.
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Value signed-right-shifted " << shift
<< " beyond width of " << lp->expr_width()
<< ". Elaborate as replicated top bit." << endl;
tmp = new NetEConst(verinum(lp->expr_width()-1));
tmp->set_line(*this);
tmp = new NetESelect(lp, tmp, 1);
tmp->cast_signed(true);
tmp->set_line(*this);
tmp = pad_to_width(tmp, use_wid);
tmp->set_line(*this);
return tmp;
} else if (shift >= 0) {
// Signed right shift.
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "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;
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);
tmp->set_line(*this);
return tmp;
} else if ((0-shift) >= use_wid) {
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Value signed-right-shifted " << shift
<< " beyond width of " << use_wid
<< "." << endl;
tmp = make_const_0(use_wid);
tmp->set_line(*this);
return tmp;
}
}
// Falback, handle the general case.
if (expr_wid > 0)
lp = pad_to_width(lp, expr_wid);
tmp = new NetEBShift(op_, lp, rp);
tmp->set_line(*this);
return tmp;
}
unsigned PEBComp::test_width(Design*, NetScope*,unsigned, unsigned, bool&) const
{
return 1;

View File

@ -1584,6 +1584,29 @@ NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const
return lval_->elaborate_lval(des, scope, false);
}
NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope,
unsigned lv_width) const
{
ivl_assert(*this, rval_);
/* Find out what the r-value width is going to be. We guess it
will be the l-value width, but it may turn out to be
something else based on self-determined widths inside. */
unsigned use_width = lv_width;
bool unsized_flag = false;
unsigned tmp_width = rval()->test_width(des, scope, use_width, use_width, unsized_flag);
if (tmp_width > use_width)
use_width = tmp_width;
/* Now elaborate to the expected width. Pass the lwidth to
prune any constant result to fit with the lvalue at hand. */
NetExpr*rv = elab_and_eval(des, scope, rval_, use_width, lv_width);
if (rv == 0) return 0;
return rv;
}
/*
* This function elaborates delay expressions. This is a little
* different from normal elaboration because the result may need to be
@ -1665,23 +1688,9 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
delay = elaborate_delay_expr(delay_, des, scope);
assert(rval());
/* Elaborate the r-value expression, then try to evaluate it. */
/* Find out what the r-value width is going to be. We guess it
will be the l-value width, but it may turn out to be
something else based on self-determined widths inside. */
unsigned use_width = lv->lwidth();
bool unsized_flag = false;
use_width = rval()->test_width(des, scope, use_width, use_width, unsized_flag);
/* Now elaborate to the expected width. Pass the lwidth to
prune any constant result to fit with the lvalue at hand. */
NetExpr*rv = elab_and_eval(des, scope, rval(), use_width, lv->lwidth());
NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv));
if (rv == 0) return 0;
assert(rv);
/* Rewrite delayed assignments as assignments that are
delayed. For example, a = #<d> b; becomes:
@ -1802,12 +1811,7 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
NetAssign_*lv = elaborate_lval(des, scope);
if (lv == 0) return 0;
assert(rval());
/* Elaborate and precalculate the r-value. */
NetExpr*rv = elab_and_eval(des, scope, rval(), count_lval_width(lv));
if (rv == 0)
return 0;
NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv));
/* Handle the (common) case that the r-value is a vector. This
includes just about everything but reals. In this case, we

View File

@ -200,6 +200,13 @@ NetEConst* make_const_x(unsigned long wid)
return resx;
}
NetEConst* make_const_0(unsigned long wid)
{
verinum xxx (verinum::V0, wid);
NetEConst*resx = new NetEConst(xxx);
return resx;
}
NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid)
{
verinum xxx (verinum::Vx, wid);

View File

@ -114,6 +114,7 @@ extern NetExpr*make_sub_expr(long val, NetExpr*expr);
* Make a NetEConst object that contains only X bits.
*/
extern NetEConst*make_const_x(unsigned long wid);
extern NetEConst*make_const_0(unsigned long wid);
/*
* Make A const net