Skip to content

Commit 8427b1f

Browse files
author
Remi Delmas
committed
CONTRACTS: rearchitecture code for loop contracts
- Factor out some methods in dfcc_spec_functionst - Move the code generation methods from dfcc_contract_functionst to dfcc_contract_clauses_codegent. - Update Makefile with new class. - Propagate interface changes to top level class
1 parent 1ad6ecc commit 8427b1f

11 files changed

+677
-362
lines changed

src/goto-instrument/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SRC = accelerate/accelerate.cpp \
2626
contracts/dynamic-frames/dfcc_lift_memory_predicates.cpp \
2727
contracts/dynamic-frames/dfcc_instrument.cpp \
2828
contracts/dynamic-frames/dfcc_spec_functions.cpp \
29+
contracts/dynamic-frames/dfcc_contract_clauses_codegen.cpp \
2930
contracts/dynamic-frames/dfcc_contract_functions.cpp \
3031
contracts/dynamic-frames/dfcc_wrapper_program.cpp \
3132
contracts/dynamic-frames/dfcc_contract_handler.cpp \

src/goto-instrument/contracts/dynamic-frames/dfcc.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,21 @@ dfcct::dfcct(
191191
instrument(goto_model, message_handler, utils, library),
192192
memory_predicates(goto_model, utils, library, instrument, message_handler),
193193
spec_functions(goto_model, message_handler, utils, library, instrument),
194+
contract_clauses_codegen(
195+
goto_model,
196+
message_handler,
197+
utils,
198+
library,
199+
spec_functions),
194200
contract_handler(
195201
goto_model,
196202
message_handler,
197203
utils,
198204
library,
199205
instrument,
200206
memory_predicates,
201-
spec_functions),
207+
spec_functions,
208+
contract_clauses_codegen),
202209
swap_and_wrap(
203210
goto_model,
204211
message_handler,
@@ -483,6 +490,8 @@ void dfcct::instrument_other_functions()
483490

484491
goto_model.goto_functions.update();
485492

493+
// TODO specialise the library functions for the max size of
494+
// loop and function contracts
486495
if(to_check.has_value())
487496
{
488497
log.status() << "Specializing cprover_contracts functions for assigns "

src/goto-instrument/contracts/dynamic-frames/dfcc.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Author: Remi Delmas, [email protected]
3333
#include <util/irep.h>
3434
#include <util/message.h>
3535

36+
#include "dfcc_contract_clauses_codegen.h"
3637
#include "dfcc_contract_handler.h"
3738
#include "dfcc_instrument.h"
3839
#include "dfcc_library.h"
@@ -208,13 +209,15 @@ class dfcct
208209
message_handlert &message_handler;
209210
messaget log;
210211

211-
// hold the global state of the transformation (caches etc.)
212+
// Singletons that hold the global state of the program transformation
213+
// (caches etc.)
212214
dfcc_utilst utils;
213215
dfcc_libraryt library;
214216
namespacet ns;
215217
dfcc_instrumentt instrument;
216218
dfcc_lift_memory_predicatest memory_predicates;
217219
dfcc_spec_functionst spec_functions;
220+
dfcc_contract_clauses_codegent contract_clauses_codegen;
218221
dfcc_contract_handlert contract_handler;
219222
dfcc_swap_and_wrapt swap_and_wrap;
220223

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*******************************************************************\
2+
3+
Module: Dynamic frame condition checking for function contracts
4+
5+
Author: Remi Delmas, [email protected]
6+
Date: February 2023
7+
8+
\*******************************************************************/
9+
#include "dfcc_contract_clauses_codegen.h"
10+
11+
#include <util/expr_util.h>
12+
#include <util/fresh_symbol.h>
13+
#include <util/invariant.h>
14+
#include <util/mathematical_expr.h>
15+
#include <util/namespace.h>
16+
#include <util/pointer_offset_size.h>
17+
#include <util/std_expr.h>
18+
19+
#include <goto-programs/goto_model.h>
20+
21+
#include <ansi-c/c_expr.h>
22+
#include <goto-instrument/contracts/utils.h>
23+
#include <langapi/language_util.h>
24+
25+
#include "dfcc_library.h"
26+
#include "dfcc_spec_functions.h"
27+
#include "dfcc_utils.h"
28+
29+
dfcc_contract_clauses_codegent::dfcc_contract_clauses_codegent(
30+
goto_modelt &goto_model,
31+
message_handlert &message_handler,
32+
dfcc_utilst &utils,
33+
dfcc_libraryt &library,
34+
dfcc_spec_functionst &spec_functions)
35+
: goto_model(goto_model),
36+
message_handler(message_handler),
37+
log(message_handler),
38+
utils(utils),
39+
library(library),
40+
spec_functions(spec_functions),
41+
ns(goto_model.symbol_table)
42+
{
43+
}
44+
45+
void dfcc_contract_clauses_codegent::gen_spec_assigns_instructions(
46+
const irep_idt &language_mode,
47+
const exprt::operandst &assigns_clause,
48+
goto_programt &dest)
49+
{
50+
for(const auto &expr : assigns_clause)
51+
{
52+
if(can_cast_expr<conditional_target_group_exprt>(expr))
53+
{
54+
encode_assignable_target_group(
55+
language_mode, to_conditional_target_group_expr(expr), dest);
56+
}
57+
else
58+
{
59+
encode_assignable_target(language_mode, expr, dest);
60+
}
61+
}
62+
63+
// inline resulting program and check for loops
64+
inline_and_check_warnings(dest);
65+
PRECONDITION_WITH_DIAGNOSTICS(
66+
utils.has_no_loops(dest),
67+
"loops in assigns clause specification functions must be unwound before "
68+
"contracts instrumentation");
69+
}
70+
71+
void dfcc_contract_clauses_codegent::gen_spec_frees_instructions(
72+
const irep_idt &language_mode,
73+
const exprt::operandst &frees_clause,
74+
goto_programt &dest)
75+
{
76+
for(const auto &expr : frees_clause)
77+
{
78+
if(can_cast_expr<conditional_target_group_exprt>(expr))
79+
{
80+
encode_freeable_target_group(
81+
language_mode, to_conditional_target_group_expr(expr), dest);
82+
}
83+
else
84+
{
85+
encode_freeable_target(language_mode, expr, dest);
86+
}
87+
}
88+
89+
// inline resulting program and check for loops
90+
inline_and_check_warnings(dest);
91+
PRECONDITION_WITH_DIAGNOSTICS(
92+
utils.has_no_loops(dest),
93+
"loops in assigns clause specification functions must be unwound before "
94+
"contracts instrumentation");
95+
}
96+
97+
void dfcc_contract_clauses_codegent::encode_assignable_target_group(
98+
const irep_idt &language_mode,
99+
const conditional_target_group_exprt &group,
100+
goto_programt &dest)
101+
{
102+
const source_locationt &source_location = group.source_location();
103+
104+
// clean up side effects from the condition expression if needed
105+
cleanert cleaner(goto_model.symbol_table, log.get_message_handler());
106+
exprt condition(group.condition());
107+
if(has_subexpr(condition, ID_side_effect))
108+
cleaner.clean(condition, dest, language_mode);
109+
110+
// Jump target if condition is false
111+
auto goto_instruction = dest.add(
112+
goto_programt::make_incomplete_goto(not_exprt{condition}, source_location));
113+
114+
for(const auto &target : group.targets())
115+
encode_assignable_target(language_mode, target, dest);
116+
117+
auto label_instruction = dest.add(goto_programt::make_skip(source_location));
118+
goto_instruction->complete_goto(label_instruction);
119+
}
120+
121+
void dfcc_contract_clauses_codegent::encode_assignable_target(
122+
const irep_idt &language_mode,
123+
const exprt &target,
124+
goto_programt &dest)
125+
{
126+
const source_locationt &source_location = target.source_location();
127+
128+
if(can_cast_expr<side_effect_expr_function_callt>(target))
129+
{
130+
// A function call target `foo(params)` becomes `CALL foo(params);`
131+
const auto &funcall = to_side_effect_expr_function_call(target);
132+
code_function_callt code_function_call(to_symbol_expr(funcall.function()));
133+
auto &arguments = code_function_call.arguments();
134+
for(auto &arg : funcall.arguments())
135+
arguments.emplace_back(arg);
136+
dest.add(
137+
goto_programt::make_function_call(code_function_call, source_location));
138+
}
139+
else if(is_assignable(target))
140+
{
141+
// An lvalue `target` becomes
142+
//` CALL __CPROVER_assignable(&target, sizeof(target), is_ptr_to_ptr);`
143+
const auto &size =
144+
size_of_expr(target.type(), namespacet(goto_model.symbol_table));
145+
146+
if(!size.has_value())
147+
{
148+
throw invalid_source_file_exceptiont{
149+
"no definite size for lvalue assigns clause target " +
150+
from_expr_using_mode(ns, language_mode, target),
151+
target.source_location()};
152+
}
153+
// we have to build the symbol manually because it might not
154+
// be present in the symbol table if the user program does not already
155+
// use it.
156+
code_function_callt code_function_call(
157+
symbol_exprt(CPROVER_PREFIX "assignable", empty_typet()));
158+
auto &arguments = code_function_call.arguments();
159+
160+
// ptr
161+
arguments.emplace_back(typecast_exprt::conditional_cast(
162+
address_of_exprt{target}, pointer_type(empty_typet())));
163+
164+
// size
165+
arguments.emplace_back(size.value());
166+
167+
// is_ptr_to_ptr
168+
arguments.emplace_back(make_boolean_expr(target.type().id() == ID_pointer));
169+
170+
dest.add(
171+
goto_programt::make_function_call(code_function_call, source_location));
172+
}
173+
else
174+
{
175+
// any other type of target is unsupported
176+
throw invalid_source_file_exceptiont(
177+
"unsupported assigns clause target " +
178+
from_expr_using_mode(ns, language_mode, target),
179+
target.source_location());
180+
}
181+
}
182+
183+
void dfcc_contract_clauses_codegent::encode_freeable_target_group(
184+
const irep_idt &language_mode,
185+
const conditional_target_group_exprt &group,
186+
goto_programt &dest)
187+
{
188+
const source_locationt &source_location = group.source_location();
189+
190+
// clean up side effects from the condition expression if needed
191+
cleanert cleaner(goto_model.symbol_table, log.get_message_handler());
192+
exprt condition(group.condition());
193+
if(has_subexpr(condition, ID_side_effect))
194+
cleaner.clean(condition, dest, language_mode);
195+
196+
// Jump target if condition is false
197+
auto goto_instruction = dest.add(
198+
goto_programt::make_incomplete_goto(not_exprt{condition}, source_location));
199+
200+
for(const auto &target : group.targets())
201+
encode_freeable_target(language_mode, target, dest);
202+
203+
auto label_instruction = dest.add(goto_programt::make_skip(source_location));
204+
goto_instruction->complete_goto(label_instruction);
205+
}
206+
207+
void dfcc_contract_clauses_codegent::encode_freeable_target(
208+
const irep_idt &language_mode,
209+
const exprt &target,
210+
goto_programt &dest)
211+
{
212+
const source_locationt &source_location = target.source_location();
213+
214+
if(can_cast_expr<side_effect_expr_function_callt>(target))
215+
{
216+
const auto &funcall = to_side_effect_expr_function_call(target);
217+
if(can_cast_expr<symbol_exprt>(funcall.function()))
218+
{
219+
// for calls to user-defined functions a call expression `foo(params)`
220+
// becomes an instruction `CALL foo(params);`
221+
code_function_callt code_function_call(
222+
to_symbol_expr(funcall.function()));
223+
auto &arguments = code_function_call.arguments();
224+
for(auto &arg : funcall.arguments())
225+
arguments.emplace_back(arg);
226+
dest.add(
227+
goto_programt::make_function_call(code_function_call, source_location));
228+
}
229+
}
230+
else if(can_cast_type<pointer_typet>(target.type()))
231+
{
232+
// A plain `target` becomes `CALL __CPROVER_freeable(target);`
233+
code_function_callt code_function_call(
234+
utils.get_function_symbol(CPROVER_PREFIX "freeable").symbol_expr());
235+
auto &arguments = code_function_call.arguments();
236+
arguments.emplace_back(target);
237+
238+
dest.add(
239+
goto_programt::make_function_call(code_function_call, source_location));
240+
}
241+
else
242+
{
243+
// any other type of target is unsupported
244+
throw invalid_source_file_exceptiont(
245+
"unsupported frees clause target " +
246+
from_expr_using_mode(ns, language_mode, target),
247+
target.source_location());
248+
}
249+
}
250+
251+
void dfcc_contract_clauses_codegent::inline_and_check_warnings(
252+
goto_programt &goto_program)
253+
{
254+
std::set<irep_idt> no_body;
255+
std::set<irep_idt> missing_function;
256+
std::set<irep_idt> recursive_call;
257+
std::set<irep_idt> not_enough_arguments;
258+
259+
utils.inline_program(
260+
goto_program,
261+
no_body,
262+
recursive_call,
263+
missing_function,
264+
not_enough_arguments);
265+
266+
// check that the only no body / missing functions are the cprover builtins
267+
for(const auto &id : no_body)
268+
{
269+
INVARIANT(
270+
library.is_front_end_builtin(id),
271+
"no body for '" + id2string(id) + "' when inlining goto program");
272+
}
273+
274+
for(auto it : missing_function)
275+
{
276+
INVARIANT(
277+
library.is_front_end_builtin(it),
278+
"missing function '" + id2string(it) + "' when inlining goto program");
279+
}
280+
281+
INVARIANT(
282+
recursive_call.size() == 0,
283+
"recursive calls found when inlining goto program");
284+
285+
INVARIANT(
286+
not_enough_arguments.size() == 0,
287+
"not enough arguments when inlining goto program");
288+
}

0 commit comments

Comments
 (0)