Skip to content

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

Merged

Conversation

thomasspriggs
Copy link
Contributor

This PR adds separate overflow expr classes. It is split out from #6736 so that the expr and smt changes can be reviewed separately.

  • Each commit message has a non-empty body, explaining why the change was made.
  • Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
  • Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
  • My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@codecov
Copy link

codecov bot commented Mar 22, 2022

Codecov Report

Merging #6744 (c0f179c) into develop (69eded9) will increase coverage by 0.00%.
The diff coverage is 90.37%.

@@           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     
Impacted Files Coverage Δ
src/ansi-c/goto_check_c.cpp 91.61% <0.00%> (ø)
src/solvers/flattening/boolbv_overflow.cpp 50.58% <69.69%> (+1.80%) ⬆️
src/util/bitvector_expr.h 96.73% <95.23%> (-0.30%) ⬇️
src/ansi-c/c_typecheck_expr.cpp 77.06% <100.00%> (ø)
src/ansi-c/expr2c.cpp 66.62% <100.00%> (-0.02%) ⬇️
src/solvers/flattening/boolbv.cpp 80.56% <100.00%> (-0.06%) ⬇️
src/solvers/smt2/smt2_conv.cpp 67.48% <100.00%> (+0.02%) ⬆️
src/util/simplify_expr.cpp 85.72% <100.00%> (ø)
unit/util/bitvector_expr.cpp 100.00% <100.00%> (ø)
src/util/pointer_predicates.h 75.00% <0.00%> (-25.00%) ⬇️
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4987df3...c0f179c. Read the comment docs.

@@ -778,6 +794,66 @@ inline binary_overflow_exprt &to_binary_overflow_expr(exprt &expr)
return ret;
}

class plus_overflow_exprt : public binary_overflow_exprt
Copy link
Member

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?

Copy link
Collaborator

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_exprts with a computed id.

Copy link
Contributor Author

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.

Comment on lines 16 to 42
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);
Copy link
Collaborator

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.)

Copy link
Contributor Author

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.

Copy link
Contributor Author

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(
Copy link
Contributor

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.

@thomasspriggs thomasspriggs merged commit 4996426 into diffblue:develop Mar 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants