Skip to content

Skip function parameters and auxiliary variables when checking assigns clauses and check loop-freeness #6527

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

remi-delmas-3000
Copy link
Collaborator

@remi-delmas-3000 remi-delmas-3000 commented Dec 11, 2021

This PR skips some unnecessary instrumentation when checking assigns clauses.

Skipping function parameters

Assignments to variables storing function parameters, as given by symbolt::is_parameter are always legal so we detect them and skip their instrumentation. We do however still add their CARs to the function's write set since they may be aliased and their addresses passed to sub-function calls.

Skipping auxiliary variables

Auxiliary variables, as given by symbolt::is_auxiliary are introduced by the goto compiler to decompose expressions in subexpressions, to store return values for function calls, etc. are only assigned once, cannot have aliases and are really just names for expressions. So we do not add them to the write set anymore, and we skip their assignments in the instrumentation.

Checking loop freeness

We add a check for loop-freeness of the goto-program before attempting a function's assigns clause instrumentation. The assigns clause instrumentation is not sound in the presence of loops, and we did not check this condition before.
To detect loops we compute strongly connected components of the function's control flow graph and detect components that have a cardinality strictly greater than 1.

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

@remi-delmas-3000 remi-delmas-3000 force-pushed the exclude-auxiliaries-from-assigns-clause-checking branch from 2c9c2aa to 7fa006e Compare December 11, 2021 01:26
@codecov
Copy link

codecov bot commented Dec 11, 2021

Codecov Report

Merging #6527 (bbe628b) into develop (5f33908) will increase coverage by 0.00%.
The diff coverage is 90.12%.

Impacted file tree graph

@@           Coverage Diff            @@
##           develop    #6527   +/-   ##
========================================
  Coverage    75.98%   75.98%           
========================================
  Files         1578     1578           
  Lines       180910   180976   +66     
========================================
+ Hits        137467   137522   +55     
- Misses       43443    43454   +11     
Impacted Files Coverage Δ
src/goto-instrument/contracts/contracts.h 100.00% <ø> (ø)
src/goto-instrument/contracts/utils.h 100.00% <ø> (ø)
src/goto-instrument/contracts/utils.cpp 89.83% <84.31%> (-4.20%) ⬇️
src/goto-instrument/contracts/contracts.cpp 94.72% <100.00%> (-0.33%) ⬇️

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 eeddb3f...bbe628b. Read the comment docs.

@feliperodri feliperodri added aws Bugs or features of importance to AWS CBMC users Code Contracts Function and loop contracts labels Dec 13, 2021
Comment on lines +18 to +27
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
arr[5] = 5;
arr[6] = 6;
arr[7] = 7;
arr[8] = 8;
arr[9] = 9;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why remove this loop?

Copy link
Collaborator Author

@remi-delmas-3000 remi-delmas-3000 Dec 13, 2021

Choose a reason for hiding this comment

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

This PR adds a loop freeness check before running the instrumentation, so the loop needs to be eliminated to satisfy this loop-freeness check.
This manual unrolling of loops is just a quick and dirty fix. A better solution would be to modify chain.sh add a pass of loop unrolling or loop contracts instrumentation.

Comment on lines 1160 to 1185
INVARIANT(
is_loop_free(goto_program),
"Loops remain in function '" + id2string(function) +
"', assigns clause checking instrumentation cannot be applied.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice!

@remi-delmas-3000 remi-delmas-3000 force-pushed the exclude-auxiliaries-from-assigns-clause-checking branch from f069a4c to f0f6dca Compare December 13, 2021 16:09
@remi-delmas-3000 remi-delmas-3000 force-pushed the exclude-auxiliaries-from-assigns-clause-checking branch 2 times, most recently from ad4d9db to 08c7f2a Compare December 13, 2021 18:24
@remi-delmas-3000 remi-delmas-3000 force-pushed the exclude-auxiliaries-from-assigns-clause-checking branch from 08c7f2a to bbe628b Compare December 13, 2021 21:43
Copy link
Collaborator

@feliperodri feliperodri left a comment

Choose a reason for hiding this comment

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

Only a couple of code suggestions.

if(check_for_looped_mallocs(function_obj->second.body))
auto &function_body = function_obj->second.body;

if(check_for_looped_mallocs(function_body))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This check is no longer necessary, you can remove it. It checks whether there is malloc within loops, but we assume contract instrumentation happens after all loops have been unrolled.

Comment on lines +175 to +189
optionalt<symbol_exprt> sym = {};

// extract symbol
if(instruction_it->is_assign())
{
const auto &lhs = instruction_it->assign_lhs();
if(can_cast_expr<symbol_exprt>(lhs))
sym = to_symbol_expr(lhs);
}

// check condition
if(sym.has_value())
return ns.lookup(sym.value().get_identifier()).is_parameter;

return false;
Copy link
Collaborator

@feliperodri feliperodri Dec 14, 2021

Choose a reason for hiding this comment

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

Suggested change
optionalt<symbol_exprt> sym = {};
// extract symbol
if(instruction_it->is_assign())
{
const auto &lhs = instruction_it->assign_lhs();
if(can_cast_expr<symbol_exprt>(lhs))
sym = to_symbol_expr(lhs);
}
// check condition
if(sym.has_value())
return ns.lookup(sym.value().get_identifier()).is_parameter;
return false;
if(instruction_it->is_assign() && can_cast_expr<symbol_exprt>(instruction_it->assign_lhs()))
{
// Extract symbol from lhs
auto lhs_sym = to_symbol_expr(instruction_it->assign_lhs());
// Check whether is parameter
if(lhs_sym.has_value())
return ns.lookup(lhs_sym.value().get_identifier()).is_parameter;
}
return false;

Copy link
Collaborator

Choose a reason for hiding this comment

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

On top of what Felipe suggested: there's no need for the optionalt anymore.

Comment on lines +192 to +215
bool is_auxiliary_decl_dead_or_assign(
const goto_programt::targett &instruction_it,
namespacet &ns)
{
optionalt<symbol_exprt> sym = {};

// extract symbol
if(instruction_it->is_decl())
sym = instruction_it->decl_symbol();
else if(instruction_it->is_dead())
sym = instruction_it->dead_symbol();
else if(instruction_it->is_assign())
{
const auto &lhs = instruction_it->assign_lhs();
if(can_cast_expr<symbol_exprt>(lhs))
sym = to_symbol_expr(lhs);
}

// check condition
if(sym.has_value())
return ns.lookup(sym.value().get_identifier()).is_auxiliary;

return false;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
bool is_auxiliary_decl_dead_or_assign(
const goto_programt::targett &instruction_it,
namespacet &ns)
{
optionalt<symbol_exprt> sym = {};
// extract symbol
if(instruction_it->is_decl())
sym = instruction_it->decl_symbol();
else if(instruction_it->is_dead())
sym = instruction_it->dead_symbol();
else if(instruction_it->is_assign())
{
const auto &lhs = instruction_it->assign_lhs();
if(can_cast_expr<symbol_exprt>(lhs))
sym = to_symbol_expr(lhs);
}
// check condition
if(sym.has_value())
return ns.lookup(sym.value().get_identifier()).is_auxiliary;
return false;
}
bool is_auxiliary_decl_dead_or_assign(
const goto_programt::targett &instruction_it,
namespacet &ns)
{
// Extract symbol
optionalt<symbol_exprt> sym = {};
if(instruction_it->is_assign() && can_cast_expr<symbol_exprt>(instruction_it->assign_lhs()))
sym = to_symbol_expr(instruction_it->assign_lhs());
else if(instruction_it->is_decl())
sym = instruction_it->decl_symbol();
else if(instruction_it->is_dead())
sym = instruction_it->dead_symbol();
else
return false;
// Check whether is auxiliary
return ns.lookup(sym.value().get_identifier()).is_auxiliary;
}

Comment on lines +217 to +229
void simplify_gotos(goto_programt &goto_program, namespacet &ns)
{
for(auto &instruction : goto_program.instructions)
{
if(instruction.is_goto())
{
const auto &condition = instruction.get_condition();
const auto &simplified = simplify_expr(condition, ns);
if(simplified.is_false())
instruction.turn_into_skip();
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
void simplify_gotos(goto_programt &goto_program, namespacet &ns)
{
for(auto &instruction : goto_program.instructions)
{
if(instruction.is_goto())
{
const auto &condition = instruction.get_condition();
const auto &simplified = simplify_expr(condition, ns);
if(simplified.is_false())
instruction.turn_into_skip();
}
}
}
void simplify_gotos(goto_programt &goto_program, namespacet &ns)
{
for(auto &it : goto_program.instructions)
if(it.is_goto() && simplify_expr(it.get_condition(), ns).is_false())
it.turn_into_skip();
}

auto size = scc_size[scc_id];
if(size > 1)
{
all_size_one = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could add a regression test to explore these paths.

Comment on lines +317 to +318
// do not skip checks on function parameter assignments
false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we see a need to comment on the Boolean value then it may be better to introduce an enum that has informative values. If we always pass constant values, then it might be even better to use a template parameter.

Comment on lines +115 to +123
/// \param is_auxiliary: Do not print symbol in traces if true (default = true)
/// \return The new symbolt object.
const symbolt &new_tmp_symbol(
const typet &type,
const source_locationt &location,
const irep_idt &mode,
symbol_table_baset &symtab,
std::string suffix = "tmp_cc",
bool is_auxiliary = false);
bool is_auxiliary = true);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this being changed as part of this PR?

Comment on lines +231 to +239
bool is_loop_free(
const goto_programt &goto_program,
namespacet &ns,
messaget &log)
{
// create cfg from instruction list
cfg_baset<empty_cfg_nodet> cfg;
cfg(goto_program);

Copy link
Collaborator

Choose a reason for hiding this comment

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

You wouldn't have quite as elaborate error messages, but wouldn't a simple test of all instructions for is_backwards_goto() do the trick?

@remi-delmas-3000
Copy link
Collaborator Author

superseded by #6533

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aws Bugs or features of importance to AWS CBMC users Code Contracts Function and loop contracts enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants