-
Notifications
You must be signed in to change notification settings - Fork 274
Add separate overflow expr classes #6744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add separate overflow expr classes #6744
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #6744 +/- ##
========================================
Coverage 76.86% 76.87%
========================================
Files 1589 1590 +1
Lines 183809 183887 +78
========================================
+ Hits 141293 141364 +71
- Misses 42516 42523 +7
Continue to review full report at Codecov.
|
@@ -778,6 +794,66 @@ inline binary_overflow_exprt &to_binary_overflow_expr(exprt &expr) | |||
return ret; | |||
} | |||
|
|||
class plus_overflow_exprt : public binary_overflow_exprt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these classes be used somewhere in the existing code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had previously taken a look and it seems we consistently take shortcuts by creating binary_overflow_exprt
s with a computed id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have now pushed additional commits which use the new classes throughout the existing code where the IDs were previously being checked.
const auto plus_or_minus_conversion = | ||
[&]( | ||
const binary_overflow_exprt &overflow_expr, | ||
const std::function<literalt( | ||
bv_utilst *, const bvt &, const bvt &, bv_utilst::representationt)> | ||
&bv_util_overflow) { | ||
const bvt &bv0 = convert_bv(overflow_expr.lhs()); | ||
const bvt &bv1 = convert_bv(overflow_expr.rhs()); | ||
|
||
if(bv0.size() != bv1.size()) | ||
return SUB::convert_rest(expr); | ||
|
||
bv_utilst::representationt rep = | ||
overflow_expr.lhs().type().id() == ID_signedbv | ||
? bv_utilst::representationt::SIGNED | ||
: bv_utilst::representationt::UNSIGNED; | ||
|
||
return bv_util_overflow(&bv_utils, bv0, bv1, rep); | ||
}; | ||
if( | ||
const auto plus_overflow = expr_try_dynamic_cast<plus_overflow_exprt>(expr)) | ||
{ | ||
const auto &overflow_expr = to_binary_expr(expr); | ||
|
||
const bvt &bv0 = convert_bv(overflow_expr.lhs()); | ||
const bvt &bv1 = convert_bv(overflow_expr.rhs()); | ||
|
||
if(bv0.size()!=bv1.size()) | ||
return SUB::convert_rest(expr); | ||
|
||
bv_utilst::representationt rep = | ||
overflow_expr.lhs().type().id() == ID_signedbv | ||
? bv_utilst::representationt::SIGNED | ||
: bv_utilst::representationt::UNSIGNED; | ||
|
||
return expr.id()==ID_overflow_minus? | ||
bv_utils.overflow_sub(bv0, bv1, rep): | ||
bv_utils.overflow_add(bv0, bv1, rep); | ||
return plus_or_minus_conversion(*plus_overflow, &bv_utilst::overflow_add); | ||
} | ||
if(const auto minus = expr_try_dynamic_cast<minus_overflow_exprt>(expr)) | ||
{ | ||
return plus_or_minus_conversion(*minus, &bv_utilst::overflow_sub); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the lambda helps readability here. I'd perhaps favour two branches with almost identical code, or else a slightly larger refactor: all the branches in boolbvt::convert_overflow
need to convert the operands, perhaps also need to determine signed/unsigned representation. And we might opt to be consistent in whether we support (multiply) or don't support (other cases) operands of different width. (I have no idea why we would support them.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the more ideal refactor would be to remove more of the duplication between all of the branches here. However given that I have approvals on this PR and I have other work which is based on top of this, I am going to merge this as-is and then come back to refactoring boolbvt::convert_overflow
as a follow-up PR. That way the larger refactor can be considered in isolation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about mismatched operand widths, I think there is the potential for a simpler/smaller formula for multiplying two operands of mismatched but shorter widths instead of 2 operands of matched but larger widths due to being extended to match. Any potential benefits to this are speculation on my part and it seems like a case which may be rare in source code or even always eliminated by implicit type casts before we reach this stage.
@@ -707,6 +707,10 @@ class binary_overflow_exprt : public binary_predicate_exprt | |||
binary_overflow_exprt(exprt _lhs, const irep_idt &kind, exprt _rhs) | |||
: binary_predicate_exprt(std::move(_lhs), make_id(kind), std::move(_rhs)) | |||
{ | |||
INVARIANT( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I quite like this.
I favour things being correct by construction, and subsequently, for the invariants to be set as early as possible.
This PR adds separate overflow expr classes. It is split out from #6736 so that the
expr
and smt changes can be reviewed separately.