Skip to content

Constant propagator: improve GOTO condition propagation #2522

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

Conversation

smowton
Copy link
Contributor

@smowton smowton commented Jul 4, 2018

This improves two-way propagation to also account for many other common patterns that imply an equality (e.g. x != y -> x == !y when the operands are of boolean types, or !x --> x == TRUE). I also fix a case where the fixpoint loop around the && case would loop forever because assign_rec can both enlarge and shrink its value-set -- for example, the condition x == 5 && **unknown == (void*)also_unknown would result in repeatedly adding x -> 5 and the clobbering the entire value set. I fix that by noting that in the context of two-way propagation, since there's no write operation taking place we should never shrink the value set.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passed Diffblue compatibility checks (cbmc commit: d61ce78).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/78115950

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't completely understand the two way propagation, but this looks reasonable to me.

{
operator()(goto_function, ns);
replace(goto_function, ns);
}

dirtyt dirty;

std::function<bool(const exprt &, const namespacet &)> should_track_value;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of this being a public member. IMHO would be better to have this private. If this needs to be called from the outside, we could just call it should_track_value_handler and have a method should_track_value call this. It being public just invites shenanigans with people manually overwriting it.

if(lhs.is_constant() && !rhs.is_constant())
std::swap(lhs, rhs);

if(!(rhs.is_constant() && !lhs.is_constant()))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be clearer to expand the not here: lhs.is_constant() || !rhs.is_constant().

// (int)var xx 0 ==> var xx (_Bool)0 or similar
// Note this is restricted to bools because in general turning a widening
// into a narrowing typecast is not correct.
if(lhs.id() == ID_typecast && rhs.id() == ID_constant &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rhs.id() == ID_constant should always be true at this point.

// Create a program like:
// bool b0, b1, b2, ...;
// int marker;
// if(b0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible / reasonable to break this up into multiple smaller examples? Something this big is a bit difficult to follow.

@hannes-steffenhagen-diffblue
Copy link
Contributor

Marking do not merge as it's based on another commit that should go in first


symbolt main_function_symbol;
main_function_symbol.name = "main";
main_function_symbol.type = code_typet();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constructor is deprecated.


symbolt main_function_symbol;
main_function_symbol.name = "main";
main_function_symbol.type = code_typet();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constructor is deprecated.

@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch from d61ce78 to 4705ed7 Compare July 6, 2018 12:25
@smowton smowton requested a review from a team as a code owner July 6, 2018 12:25
@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch 2 times, most recently from 1af51c0 to 3eec1a7 Compare July 6, 2018 12:27
@smowton
Copy link
Contributor Author

smowton commented Jul 6, 2018

Comments mostly applied-- but this is parked for now pending #2132 landing.

@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch 2 times, most recently from 931c74d to bf3d12c Compare July 6, 2018 12:44
Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passed Diffblue compatibility checks (cbmc commit: bf3d12c).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/78301619

Copy link
Contributor

@danpoe danpoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

else
rhs = from_integer(0, rhs.type());

two_way_propagate_rec(equal_exprt(lhs, rhs), ns, cp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use assign_rec() here?

@smowton
Copy link
Contributor Author

smowton commented Sep 11, 2018

Noting this is still blocked on 2132 as noted above, I believe action @tautschnig

@tautschnig
Copy link
Collaborator

Noting this is still blocked on 2132 as noted above, I believe action @tautschnig

... which in turn is blocked by #2221 - @kroening @peterschrammel @smowton ;-)

@tautschnig
Copy link
Collaborator

In light of #2132 let me dare and go for high-level comments first:

  1. It may be worth removing the changes to two-way-propagation from Improvements to goto-analyzer's constant propagator and simplifier [blocks: #2522] #2132, and instead put this in to this PR here. Notably, that commit introduces a regression test that I'd like to have in place before any of the changes go in.
  2. There are simple improvements in this PR that help readability a lot but don't even change behaviour, such as removing the return value. Maybe that could go in a commit of its own? No tests needed as it would be covered by the regression test.
  3. Then there are strict extensions of functionality, such as dealing with negation or plain Booleans. Again, might be worth a commit of its own. That commit could nicely include tests showcasing the improvement.
  4. Finally, there is the refactoring of existing functionality, which is probably the part that requires the most care. As such it would be really nice to have all the other parts factored out so that one can very easily read that bit of code/that particular change.
  5. And then there's the test: unit tests are good, because they very selectively test functionality. But also they can be very hard to read, because they have to do a lot of work that is done by other components. As such I'd prefer to also have plain C examples. Now throwing away all the work that has gone into the unit test would also seem like a bad idea. Can we maybe have both?

tautschnig added a commit that referenced this pull request Dec 19, 2018
Improvements to goto-analyzer's constant propagator and simplifier [blocks: #2522]
@tautschnig
Copy link
Collaborator

@smowton #2132 is in, so this one should be ready to be rebased.

@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch from bf3d12c to f988768 Compare December 20, 2018 10:53
@smowton
Copy link
Contributor Author

smowton commented Dec 20, 2018

@tautschnig rebased and improved somewhat, and described the new state of affairs in the PR description -- please re-review

@tautschnig
Copy link
Collaborator

@smowton clang-format demands more work :-)

Copy link
Collaborator

@tautschnig tautschnig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do with a bunch of cleanup as indicated in comments, but otherwise ok.

}
else
{
// it's an assignment, but we don't really know what object is being written
// to on the left-hand side - bail and set all values to top to be on the
// safe side in terms of soundness
dest_values.set_to_top();
if(is_assignment)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about changing the above else to else if(is_assignment) and then even simplifying the comment a bit?

@@ -275,31 +312,87 @@ bool constant_propagator_domaint::two_way_propagate_rec(
std::cout << "two_way_propagate_rec: " << format(expr) << '\n';
#endif

bool change=false;
bool change = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change?

{
code_ifthenelset new_cond;
new_cond.cond() = test;
new_cond.then_case() = to_wrap;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the new code_ifthenelset constructor.

// b = true;
// Repeat this using bool_typet and c_bool_typet for "bool".

null_message_handlert null_out;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a global one defined for the unit tests.


code_blockt code;
code.copy_to_operands(code_declt(bool_local.symbol_expr()));
code.copy_to_operands(code_declt(c_bool_local.symbol_expr()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might use the initializer-list constructor

const exprt c_bool_true = from_integer(1, c_bool_typet(8));
c_bool_cond_block.cond() =
notequal_exprt(c_bool_local.symbol_expr(), c_bool_true);
c_bool_cond_block.then_case() =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use recently added constructor

code_assignt(c_bool_local.symbol_expr(), c_bool_true);

code.move_to_operands(bool_cond_block);
code.move_to_operands(c_bool_cond_block);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move_to_operands is deprecated

for(const exprt &test : c_bool_tests)
wrap_with_test(program, test);

null_message_handlert null_out;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use global one

@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch from f988768 to 06e6573 Compare January 2, 2019 17:05
@smowton
Copy link
Contributor Author

smowton commented Jan 2, 2019

@tautschnig @chrisr-diffblue @peterschrammel this is updated and ready for re-review.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️
Passed Diffblue compatibility checks (cbmc commit: 06e6573).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/96099635

else if(expr.id()==ID_equal)
else if(expr.id() == ID_symbol)
{
if(expr.type() == bool_typet())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use expr.type().id() == ID_bool to be consistent with what you're using above.

// into a narrowing typecast is not correct.
if(
lhs.id() == ID_typecast &&
(lhs.op0().type().id() == ID_bool || lhs.op0().type().id() == ID_c_bool))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rewrite this code a bit to do an early if(lhs.id() != ID_typecast) return; and then use to_typecast_expr instead of using the generic .op0().

static bool starts_with_x(const exprt &e, const namespacet &)
{
if(e.id() != ID_symbol)
return false;
return has_prefix(id2string(to_symbol_expr(e).get_identifier()), "x");
}

static void wrap_with_test(codet &to_wrap, const exprt &test)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this function actually still add value? Doing this one-liner inline might aid clarity.

@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch from 06e6573 to dc31f10 Compare January 3, 2019 12:52
@smowton
Copy link
Contributor Author

smowton commented Jan 3, 2019

Updated as requested (except comparison with bool_typet, which I think is better than checking for ID_bool). Will merge when CI passes.

@tautschnig
Copy link
Collaborator

(except comparison with bool_typet, which I think is better than checking for ID_bool)

I'm not disputing that, but I'm unhappy about mixing both approaches in a single commit. But this is a very minor issue indeed.

This supports propagation of booleans in more situations, including propagating "x != false"
and "IF x" like "IF x == TRUE".
@smowton smowton force-pushed the smowton/feature/constant-propagator-improvement branch from dc31f10 to 897ffda Compare January 3, 2019 13:13
@smowton
Copy link
Contributor Author

smowton commented Jan 3, 2019

Ah sure, switched to use full type comparison everywhere.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️
Passed Diffblue compatibility checks (cbmc commit: 897ffda).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/96195386

Copy link
Contributor

@chrisr-diffblue chrisr-diffblue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@smowton smowton merged commit 4b05dcb into diffblue:develop Jan 3, 2019
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.

8 participants