Short circuit logical operator to constant if possible

If the left-hand side of a logical operator is a constant that causes the
right-hand side to be short-circuited the right-hand side can be discarded
even if it is not constant.

In this case replace the expression by a constant.

E.g.
 * `0 && expr` will be replaced by a constant 0.
 * `1 || expr` will be replaced by a constant 1.
 * `0 -> expr` will be replaced by a constant 1.

Note that it is not possible to replace the expression by a constant if
only the right-hand side is a constant, even when the value of the
expression is constant. The left side still has to be evaluated for side
effects.

E.g. it is known at elaboration that `a++ && 0` will yield 0, but the
increment on `a` has to be executed regardless.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2021-12-28 14:15:22 +01:00
parent d4334139d3
commit 957e3d482f
1 changed files with 30 additions and 0 deletions

View File

@ -811,6 +811,36 @@ NetEConst* NetEBLogic::eval_arguments_(const NetExpr*l, const NetExpr*r) const
const NetEConst*lc = dynamic_cast<const NetEConst*>(l);
const NetEConst*rc = dynamic_cast<const NetEConst*>(r);
// If the left side is constant and the right side is short circuited
// replace the expression with a constant
if (rc == 0 && lc != 0) {
verinum v = lc->value();
verinum::V res = verinum::Vx;
switch (op_) {
case 'a': // Logical AND (&&)
if (v.is_zero())
res = verinum::V0;
break;
case 'o': // Logical OR (||)
if (! v.is_zero() && v.is_defined())
res = verinum::V1;
break;
case 'q': // Logical implication (->)
if (v.is_zero())
res = verinum::V1;
break;
default:
break;
}
if (res != verinum::Vx) {
NetEConst*tmp = new NetEConst(verinum(res, 1));
ivl_assert(*this, tmp);
eval_debug(this, tmp, false);
return tmp;
}
}
if (lc == 0 || rc == 0) return 0;
verinum::V lv = verinum::V0;