-
Notifications
You must be signed in to change notification settings - Fork 273
DFCC instrumentation: skip unused functions #8628
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
base: develop
Are you sure you want to change the base?
DFCC instrumentation: skip unused functions #8628
Conversation
Do not attempt to instrument functions that will never be used anyway. As we eventually use `remove_unused_functions` there is no point in trying to instrument them, and there is a scenario where the function may not have been compiled (see included test).
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #8628 +/- ##
========================================
Coverage 80.37% 80.37%
========================================
Files 1686 1686
Lines 206872 206877 +5
Branches 73 73
========================================
+ Hits 166265 166271 +6
+ Misses 40607 40606 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
@@ -272,7 +278,7 @@ void dfcct::partition_function_symbols( | |||
{ | |||
contract_symbols.insert(sym_name); | |||
} | |||
else | |||
else if(called_functions.find(sym_name) != called_functions.end()) |
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.
When the program uses function pointers find_used_functions
is not guaranteed to discover all reachable functions. Moreover users can expand function pointer calls after contract instrumentation, so we should at least inject assert(false);assume(false)
sentinel instructions in the functions we 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.
But then we use use remove_unused_functions
afterwards, which itself uses find_used_functions
to determine which functions to remove, so I wouldn't know how users would do something after contract instrumentation?
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.
Still not convinced we are totally sound. CBMC removes function pointers very late, right before the analysis, so if we remove functions that we think are unused, but could have been used as a target due to their signature, wouldn't we create a change in semantics for function pointers between contracts and basic symex ?
What about a case like this one ? Would intfun_contract
still be considered used ?
Does find_used_functions
consider a function used if its address is taken and assigned to a function pointer variable (e.g. intfun bar = baz;
)?
typedef int (*intfun)(int);
intfun __VERIFIER_nondet_intfun();
int intfun_contract(int x)
__CPROVER_requires(-12 <= x && x <= 12)
__CPROVER_ensures(__CPROVER_return_value == 2*x)
;
int foo(intfun bar)
__CPROVER_requires(__CPROVER_obeys_contract(bar, intfun_contract))
__CPROVER_ensures(__CPROVER_return_value == 24)
{
return bar(12);
}
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.
Neither find_used_functions
nor remove_unused_functions
will take function pointers into account. Yet goto-instrument
will always do function pointer removal before invoking the DFCC code. And dfcct::transform_goto_model
does (removed logging and a bunch of comments for compactness):
[...]
instrument_other_functions(); // the new use of find_used_functions happens in this method
auto assigns_clause_size = instrument.get_max_assigns_clause_size();
if(assigns_clause_size > max_assigns_clause_size)
max_assigns_clause_size = assigns_clause_size;
library.specialize(max_assigns_clause_size);
library.inhibit_front_end_builtins();
goto_model.goto_functions.update();
remove_skip(goto_model);
goto_model.goto_functions.update();
// This can prune too many functions if function pointers have not been
// yet been removed or if the entry point is not defined.
// Another solution would be to rewrite the bodies of functions that seem to
// be unreachable into assert(false);assume(false)
remove_unused_functions(goto_model, message_handler);
So, really, not a lot is happening before eventually calling remove_unused_functions
(of which I don't actually know why we are calling it in this code in the first place).
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.
The presence of the TODO in the code show that we've run into this problem before. I'll just instrument unreachable functions with assert(false).assume(false)
Do not attempt to instrument functions that will never be used anyway. As we eventually use
remove_unused_functions
there is no point in trying to instrument them, and there is a scenario where the function may not have been compiled (see included test).