From a12a6d925ac911e4731ea22e0457ba5b383e9736 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Tue, 22 Apr 2008 11:23:24 -0700 Subject: [PATCH] Fix left shift of unsized constants in sef-determined context. When left-shifting unsized constants in self-determined contexts, the constant value is normally pared down to its minimum required width. But this practically guarantees loss of bits. Instead, detect this special case and give the unsized constant a width of an integer. Still allow for the super-special case that the shifted value and the shift amount are constants. In that case, the result width (and value) can be calculated precisely and there is no need to resort to default widths. --- PExpr.h | 10 ++++---- elab_expr.cc | 64 +++++++++++++++++++++++++++++++++++++++++++++------ elab_pexpr.cc | 2 +- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/PExpr.h b/PExpr.h index 7f82b1c6b..889f53f48 100644 --- a/PExpr.h +++ b/PExpr.h @@ -544,7 +544,7 @@ class PEBinary : public PExpr { const NetExpr* decay, Link::strength_t drive0, Link::strength_t drive1) const; - virtual NetEBinary*elaborate_expr(Design*des, NetScope*, + virtual NetExpr*elaborate_expr(Design*des, NetScope*, int expr_width, bool sys_task_arg) const; virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; @@ -554,8 +554,8 @@ class PEBinary : public PExpr { PExpr*left_; PExpr*right_; - NetEBinary*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; - NetEBinary*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; + 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; static void suppress_operand_sign_if_needed_(NetExpr*lp, NetExpr*rp); @@ -621,8 +621,8 @@ class PEBComp : public PEBinary { unsigned min, unsigned lval, bool&flag) const; - NetEBinary* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + NetExpr* elaborate_expr(Design*des, NetScope*scope, + int expr_width, bool sys_task_arg) const; }; class PEBShift : public PEBinary { diff --git a/elab_expr.cc b/elab_expr.cc index e750f9203..5b9b13a18 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -96,7 +96,7 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, * and right sides, and creating one of a variety of different NetExpr * types. */ -NetEBinary* PEBinary::elaborate_expr(Design*des, NetScope*scope, +NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, int expr_wid, bool) const { assert(left_); @@ -110,7 +110,7 @@ NetEBinary* PEBinary::elaborate_expr(Design*des, NetScope*scope, return 0; } - NetEBinary*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid); + NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid); return tmp; } @@ -128,7 +128,7 @@ void PEBinary::suppress_operand_sign_if_needed_(NetExpr*lp, NetExpr*rp) lp->cast_signed(false); } -NetEBinary* PEBinary::elaborate_eval_expr_base_(Design*des, +NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des, NetExpr*lp, NetExpr*rp, int expr_wid) const @@ -146,7 +146,7 @@ NetEBinary* PEBinary::elaborate_eval_expr_base_(Design*des, * operands are elaborated as necessary, and all I need to do is make * the correct NetEBinary object and connect the parameters. */ -NetEBinary* PEBinary::elaborate_expr_base_(Design*des, +NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr*lp, NetExpr*rp, int expr_wid) const { @@ -157,7 +157,7 @@ NetEBinary* PEBinary::elaborate_expr_base_(Design*des, << *this << " expr_wid=" << expr_wid << endl; } - NetEBinary*tmp; + NetExpr*tmp; switch (op_) { default: @@ -200,6 +200,56 @@ NetEBinary* 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. + tmp = new NetEBShift(op_, lp, rp); + } + tmp->set_line(*this); + break; + case 'r': // >> case 'R': // >>> tmp = new NetEBShift(op_, lp, rp); @@ -269,8 +319,8 @@ unsigned PEBComp::test_width(Design*, NetScope*,unsigned, unsigned, bool&) const return 1; } -NetEBinary* PEBComp::elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const +NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, + int expr_width, bool sys_task_arg) const { assert(left_); assert(right_); diff --git a/elab_pexpr.cc b/elab_pexpr.cc index 804a29b65..a3d398a47 100644 --- a/elab_pexpr.cc +++ b/elab_pexpr.cc @@ -53,7 +53,7 @@ NetExpr*PEBinary::elaborate_pexpr (Design*des, NetScope*scope) const return 0; } - NetEBinary*tmp = elaborate_expr_base_(des, lp, rp, -2); + NetExpr*tmp = elaborate_expr_base_(des, lp, rp, -2); return tmp; }