Skip to content

Commit

Permalink
Fix left shift of unsized constants in sef-determined context.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
steveicarus committed Apr 22, 2008
1 parent 5ab0c20 commit a12a6d9
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 13 deletions.
10 changes: 5 additions & 5 deletions PExpr.h
Expand Up @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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 {
Expand Down
64 changes: 57 additions & 7 deletions elab_expr.cc
Expand Up @@ -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_);
Expand All @@ -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;
}

Expand All @@ -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
Expand All @@ -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
{
Expand All @@ -157,7 +157,7 @@ NetEBinary* PEBinary::elaborate_expr_base_(Design*des,
<< *this << " expr_wid=" << expr_wid << endl;
}

NetEBinary*tmp;
NetExpr*tmp;

switch (op_) {
default:
Expand Down Expand Up @@ -200,6 +200,56 @@ NetEBinary* 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.
tmp = new NetEBShift(op_, lp, rp);
}
tmp->set_line(*this);
break;

case 'r': // >>
case 'R': // >>>
tmp = new NetEBShift(op_, lp, rp);
Expand Down Expand Up @@ -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_);
Expand Down
2 changes: 1 addition & 1 deletion elab_pexpr.cc
Expand Up @@ -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;
}

Expand Down

0 comments on commit a12a6d9

Please sign in to comment.