diff --git a/PExpr.h b/PExpr.h index eef09b5f1..a06a59015 100644 --- a/PExpr.h +++ b/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: diff --git a/Statement.h b/Statement.h index 5bca41739..bdf4e07f4 100644 --- a/Statement.h +++ b/Statement.h @@ -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_; diff --git a/elab_expr.cc b/elab_expr.cc index 76d966130..2258ef78c 100644 --- a/elab_expr.cc +++ b/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 (lp)) { - if (NetEConst*rpc = dynamic_cast (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 (lp)) { + if (NetEConst*rpc = dynamic_cast (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 (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 (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; diff --git a/elaborate.cc b/elaborate.cc index 213f66443..23ce93fff 100644 --- a/elaborate.cc +++ b/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 = # 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 diff --git a/netmisc.cc b/netmisc.cc index d4c77956f..5d15f4085 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -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); diff --git a/netmisc.h b/netmisc.h index af954ebf9..cc6f9a7fe 100644 --- a/netmisc.h +++ b/netmisc.h @@ -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