-
Notifications
You must be signed in to change notification settings - Fork 274
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
2c9c2aa
to
7fa006e
Compare
Codecov Report
@@ Coverage Diff @@
## develop #6527 +/- ##
========================================
Coverage 75.98% 75.98%
========================================
Files 1578 1578
Lines 180910 180976 +66
========================================
+ Hits 137467 137522 +55
- Misses 43443 43454 +11
Continue to review full report at Codecov.
|
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; |
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.
Why remove this loop?
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.
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.
INVARIANT( | ||
is_loop_free(goto_program), | ||
"Loops remain in function '" + id2string(function) + | ||
"', assigns clause checking instrumentation cannot be applied."); |
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.
Nice!
f069a4c
to
f0f6dca
Compare
…rs and auxiliary variables
ad4d9db
to
08c7f2a
Compare
…ause instrumentation.
08c7f2a
to
bbe628b
Compare
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.
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)) |
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.
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.
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; |
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.
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; |
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.
On top of what Felipe suggested: there's no need for the optionalt
anymore.
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; | ||
} |
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.
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; | |
} |
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(); | ||
} | ||
} | ||
} |
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.
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; |
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.
We could add a regression test to explore these paths.
// do not skip checks on function parameter assignments | ||
false); |
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.
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.
/// \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); |
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.
Why is this being changed as part of this PR?
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); | ||
|
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.
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?
superseded by #6533 |
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.