Skip to content

Commit 2335333

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 2698a56 commit 2335333

11 files changed

+693
-362
lines changed

src/goto-instrument/Makefile

Lines changed: 1 addition & 0 deletions
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

Lines changed: 8 additions & 1 deletion
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,

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

Lines changed: 4 additions & 1 deletion
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

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
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+
const source_locationt &default_location,
49+
goto_programt &dest)
50+
{
51+
for(const auto &expr : assigns_clause)
52+
{
53+
if(can_cast_expr<conditional_target_group_exprt>(expr))
54+
{
55+
encode_assignable_target_group(
56+
language_mode, to_conditional_target_group_expr(expr), dest);
57+
}
58+
else
59+
{
60+
encode_assignable_target(language_mode, expr, dest);
61+
}
62+
}
63+
64+
dest.add(goto_programt::make_end_function(default_location));
65+
66+
goto_model.goto_functions.update();
67+
68+
// inline resulting program and check for loops
69+
inline_and_check_warnings(dest);
70+
PRECONDITION_WITH_DIAGNOSTICS(
71+
utils.has_no_loops(dest),
72+
"loops in assigns clause specification functions must be unwound before "
73+
"contracts instrumentation");
74+
}
75+
76+
void dfcc_contract_clauses_codegent::gen_spec_frees_instructions(
77+
const irep_idt &language_mode,
78+
const exprt::operandst &frees_clause,
79+
const source_locationt &default_location,
80+
goto_programt &dest)
81+
{
82+
for(const auto &expr : frees_clause)
83+
{
84+
if(can_cast_expr<conditional_target_group_exprt>(expr))
85+
{
86+
encode_freeable_target_group(
87+
language_mode, to_conditional_target_group_expr(expr), dest);
88+
}
89+
else
90+
{
91+
encode_freeable_target(language_mode, expr, dest);
92+
}
93+
}
94+
95+
dest.add(goto_programt::make_end_function(default_location));
96+
97+
// inline resulting program and check for loops
98+
inline_and_check_warnings(dest);
99+
PRECONDITION_WITH_DIAGNOSTICS(
100+
utils.has_no_loops(dest),
101+
"loops in assigns clause specification functions must be unwound before "
102+
"contracts instrumentation");
103+
}
104+
105+
void dfcc_contract_clauses_codegent::encode_assignable_target_group(
106+
const irep_idt &language_mode,
107+
const conditional_target_group_exprt &group,
108+
goto_programt &dest)
109+
{
110+
const source_locationt &source_location = group.source_location();
111+
112+
// clean up side effects from the condition expression if needed
113+
cleanert cleaner(goto_model.symbol_table, log.get_message_handler());
114+
exprt condition(group.condition());
115+
if(has_subexpr(condition, ID_side_effect))
116+
cleaner.clean(condition, dest, language_mode);
117+
118+
// Jump target if condition is false
119+
auto goto_instruction = dest.add(
120+
goto_programt::make_incomplete_goto(not_exprt{condition}, source_location));
121+
122+
for(const auto &target : group.targets())
123+
encode_assignable_target(language_mode, target, dest);
124+
125+
auto label_instruction = dest.add(goto_programt::make_skip(source_location));
126+
goto_instruction->complete_goto(label_instruction);
127+
}
128+
129+
void dfcc_contract_clauses_codegent::encode_assignable_target(
130+
const irep_idt &language_mode,
131+
const exprt &target,
132+
goto_programt &dest)
133+
{
134+
const source_locationt &source_location = target.source_location();
135+
136+
if(can_cast_expr<side_effect_expr_function_callt>(target))
137+
{
138+
// A function call target `foo(params)` becomes `CALL foo(params);`
139+
// ```
140+
const auto &funcall = to_side_effect_expr_function_call(target);
141+
code_function_callt code_function_call(to_symbol_expr(funcall.function()));
142+
auto &arguments = code_function_call.arguments();
143+
for(auto &arg : funcall.arguments())
144+
arguments.emplace_back(arg);
145+
dest.add(
146+
goto_programt::make_function_call(code_function_call, source_location));
147+
}
148+
else if(is_assignable(target))
149+
{
150+
// An lvalue `target` becomes
151+
//` CALL __CPROVER_assignable(&target, sizeof(target), is_ptr_to_ptr);`
152+
const auto &size =
153+
size_of_expr(target.type(), namespacet(goto_model.symbol_table));
154+
155+
if(!size.has_value())
156+
{
157+
throw invalid_source_file_exceptiont{
158+
"no definite size for lvalue assigns clause target " +
159+
from_expr_using_mode(ns, language_mode, target),
160+
target.source_location()};
161+
}
162+
// we have to build the symbol manually because it might not
163+
// be present in the symbol table if the user program does not already
164+
// use it.
165+
code_function_callt code_function_call(
166+
symbol_exprt(CPROVER_PREFIX "assignable", empty_typet()));
167+
auto &arguments = code_function_call.arguments();
168+
169+
// ptr
170+
arguments.emplace_back(typecast_exprt::conditional_cast(
171+
address_of_exprt{target}, pointer_type(empty_typet())));
172+
173+
// size
174+
arguments.emplace_back(size.value());
175+
176+
// is_ptr_to_ptr
177+
arguments.emplace_back(make_boolean_expr(target.type().id() == ID_pointer));
178+
179+
dest.add(
180+
goto_programt::make_function_call(code_function_call, source_location));
181+
}
182+
else
183+
{
184+
// any other type of target is unsupported
185+
throw invalid_source_file_exceptiont(
186+
"unsupported assigns clause target " +
187+
from_expr_using_mode(ns, language_mode, target),
188+
target.source_location());
189+
}
190+
}
191+
192+
void dfcc_contract_clauses_codegent::encode_freeable_target_group(
193+
const irep_idt &language_mode,
194+
const conditional_target_group_exprt &group,
195+
goto_programt &dest)
196+
{
197+
const source_locationt &source_location = group.source_location();
198+
199+
// clean up side effects from the condition expression if needed
200+
cleanert cleaner(goto_model.symbol_table, log.get_message_handler());
201+
exprt condition(group.condition());
202+
if(has_subexpr(condition, ID_side_effect))
203+
cleaner.clean(condition, dest, language_mode);
204+
205+
// Jump target if condition is false
206+
auto goto_instruction = dest.add(
207+
goto_programt::make_incomplete_goto(not_exprt{condition}, source_location));
208+
209+
for(const auto &target : group.targets())
210+
encode_freeable_target(language_mode, target, dest);
211+
212+
auto label_instruction = dest.add(goto_programt::make_skip(source_location));
213+
goto_instruction->complete_goto(label_instruction);
214+
}
215+
216+
void dfcc_contract_clauses_codegent::encode_freeable_target(
217+
const irep_idt &language_mode,
218+
const exprt &target,
219+
goto_programt &dest)
220+
{
221+
const source_locationt &source_location = target.source_location();
222+
223+
if(can_cast_expr<side_effect_expr_function_callt>(target))
224+
{
225+
const auto &funcall = to_side_effect_expr_function_call(target);
226+
if(can_cast_expr<symbol_exprt>(funcall.function()))
227+
{
228+
// for calls to user-defined functions
229+
// `foo(params)`
230+
//
231+
// ```
232+
// CALL foo(params);
233+
// ```
234+
code_function_callt code_function_call(
235+
to_symbol_expr(funcall.function()));
236+
auto &arguments = code_function_call.arguments();
237+
for(auto &arg : funcall.arguments())
238+
arguments.emplace_back(arg);
239+
dest.add(
240+
goto_programt::make_function_call(code_function_call, source_location));
241+
}
242+
}
243+
else if(can_cast_type<pointer_typet>(target.type()))
244+
{
245+
// A plain `target` becomes `CALL __CPROVER_freeable(target);`
246+
code_function_callt code_function_call(
247+
utils.get_function_symbol(CPROVER_PREFIX "freeable").symbol_expr());
248+
auto &arguments = code_function_call.arguments();
249+
arguments.emplace_back(target);
250+
251+
dest.add(
252+
goto_programt::make_function_call(code_function_call, source_location));
253+
}
254+
else
255+
{
256+
// any other type of target is unsupported
257+
throw invalid_source_file_exceptiont(
258+
"unsupported frees clause target " +
259+
from_expr_using_mode(ns, language_mode, target),
260+
target.source_location());
261+
}
262+
}
263+
264+
void dfcc_contract_clauses_codegent::inline_and_check_warnings(
265+
goto_programt &goto_program)
266+
{
267+
std::set<irep_idt> no_body;
268+
std::set<irep_idt> missing_function;
269+
std::set<irep_idt> recursive_call;
270+
std::set<irep_idt> not_enough_arguments;
271+
272+
utils.inline_program(
273+
goto_program,
274+
no_body,
275+
recursive_call,
276+
missing_function,
277+
not_enough_arguments);
278+
279+
// check that the only no body / missing functions are the cprover builtins
280+
for(const auto &id : no_body)
281+
{
282+
INVARIANT(
283+
library.is_front_end_builtin(id),
284+
"no body for '" + id2string(id) + "' when inlining goto program");
285+
}
286+
287+
for(auto it : missing_function)
288+
{
289+
INVARIANT(
290+
library.is_front_end_builtin(it),
291+
"missing function '" + id2string(it) + "' when inlining goto program");
292+
}
293+
294+
INVARIANT(
295+
recursive_call.size() == 0,
296+
"recursive calls found when inlining goto program");
297+
298+
INVARIANT(
299+
not_enough_arguments.size() == 0,
300+
"not enough arguments when inlining goto program");
301+
}

0 commit comments

Comments
 (0)