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:
parent
0de2dcb211
commit
e18eb32d8b
3
PExpr.h
3
PExpr.h
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
244
elab_expr.cc
244
elab_expr.cc
|
|
@ -147,8 +147,8 @@ NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des,
|
|||
* the correct NetEBinary object and connect the parameters.
|
||||
*/
|
||||
NetExpr* PEBinary::elaborate_expr_base_(Design*des,
|
||||
NetExpr*lp, NetExpr*rp,
|
||||
int expr_wid) const
|
||||
NetExpr*lp, NetExpr*rp,
|
||||
int expr_wid) const
|
||||
{
|
||||
bool flag;
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
46
elaborate.cc
46
elaborate.cc
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue