Skip to content

Commit bbf256f

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 19f352d commit bbf256f

11 files changed

+682
-361
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: 10 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,
@@ -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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Author: Remi Delmas, delmasrd@amazon.com
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: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
/*******************************************************************\
2+
3+
Module: Dynamic frame condition checking for function contracts
4+
5+
Author: Remi Delmas, delmasrd@amazon.com
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+
// ```
132+
const auto &funcall = to_side_effect_expr_function_call(target);
133+
code_function_callt code_function_call(to_symbol_expr(funcall.function()));
134+
auto &arguments = code_function_call.arguments();
135+
for(auto &arg : funcall.arguments())
136+
arguments.emplace_back(arg);
137+
dest.add(
138+
goto_programt::make_function_call(code_function_call, source_location));
139+
}
140+
else if(is_assignable(target))
141+
{
142+
// An lvalue `target` becomes
143+
//` CALL __CPROVER_assignable(&target, sizeof(target), is_ptr_to_ptr);`
144+
const auto &size =
145+
size_of_expr(target.type(), namespacet(goto_model.symbol_table));
146+
147+
if(!size.has_value())
148+
{
149+
throw invalid_source_file_exceptiont{
150+
"no definite size for lvalue assigns clause target " +
151+
from_expr_using_mode(ns, language_mode, target),
152+
target.source_location()};
153+
}
154+
// we have to build the symbol manually because it might not
155+
// be present in the symbol table if the user program does not already
156+
// use it.
157+
code_function_callt code_function_call(
158+
symbol_exprt(CPROVER_PREFIX "assignable", empty_typet()));
159+
auto &arguments = code_function_call.arguments();
160+
161+
// ptr
162+
arguments.emplace_back(typecast_exprt::conditional_cast(
163+
address_of_exprt{target}, pointer_type(empty_typet())));
164+
165+
// size
166+
arguments.emplace_back(size.value());
167+
168+
// is_ptr_to_ptr
169+
arguments.emplace_back(make_boolean_expr(target.type().id() == ID_pointer));
170+
171+
dest.add(
172+
goto_programt::make_function_call(code_function_call, source_location));
173+
}
174+
else
175+
{
176+
// any other type of target is unsupported
177+
throw invalid_source_file_exceptiont(
178+
"unsupported assigns clause target " +
179+
from_expr_using_mode(ns, language_mode, target),
180+
target.source_location());
181+
}
182+
}
183+
184+
void dfcc_contract_clauses_codegent::encode_freeable_target_group(
185+
const irep_idt &language_mode,
186+
const conditional_target_group_exprt &group,
187+
goto_programt &dest)
188+
{
189+
const source_locationt &source_location = group.source_location();
190+
191+
// clean up side effects from the condition expression if needed
192+
cleanert cleaner(goto_model.symbol_table, log.get_message_handler());
193+
exprt condition(group.condition());
194+
if(has_subexpr(condition, ID_side_effect))
195+
cleaner.clean(condition, dest, language_mode);
196+
197+
// Jump target if condition is false
198+
auto goto_instruction = dest.add(
199+
goto_programt::make_incomplete_goto(not_exprt{condition}, source_location));
200+
201+
for(const auto &target : group.targets())
202+
encode_freeable_target(language_mode, target, dest);
203+
204+
auto label_instruction = dest.add(goto_programt::make_skip(source_location));
205+
goto_instruction->complete_goto(label_instruction);
206+
}
207+
208+
void dfcc_contract_clauses_codegent::encode_freeable_target(
209+
const irep_idt &language_mode,
210+
const exprt &target,
211+
goto_programt &dest)
212+
{
213+
const source_locationt &source_location = target.source_location();
214+
215+
if(can_cast_expr<side_effect_expr_function_callt>(target))
216+
{
217+
const auto &funcall = to_side_effect_expr_function_call(target);
218+
if(can_cast_expr<symbol_exprt>(funcall.function()))
219+
{
220+
// for calls to user-defined functions
221+
// `foo(params)`
222+
//
223+
// ```
224+
// CALL foo(params);
225+
// ```
226+
code_function_callt code_function_call(
227+
to_symbol_expr(funcall.function()));
228+
auto &arguments = code_function_call.arguments();
229+
for(auto &arg : funcall.arguments())
230+
arguments.emplace_back(arg);
231+
dest.add(
232+
goto_programt::make_function_call(code_function_call, source_location));
233+
}
234+
}
235+
else if(can_cast_type<pointer_typet>(target.type()))
236+
{
237+
// A plain `target` becomes `CALL __CPROVER_freeable(target);`
238+
code_function_callt code_function_call(
239+
utils.get_function_symbol(CPROVER_PREFIX "freeable").symbol_expr());
240+
auto &arguments = code_function_call.arguments();
241+
arguments.emplace_back(target);
242+
243+
dest.add(
244+
goto_programt::make_function_call(code_function_call, source_location));
245+
}
246+
else
247+
{
248+
// any other type of target is unsupported
249+
throw invalid_source_file_exceptiont(
250+
"unsupported frees clause target " +
251+
from_expr_using_mode(ns, language_mode, target),
252+
target.source_location());
253+
}
254+
}
255+
256+
void dfcc_contract_clauses_codegent::inline_and_check_warnings(
257+
goto_programt &goto_program)
258+
{
259+
std::set<irep_idt> no_body;
260+
std::set<irep_idt> missing_function;
261+
std::set<irep_idt> recursive_call;
262+
std::set<irep_idt> not_enough_arguments;
263+
264+
utils.inline_program(
265+
goto_program,
266+
no_body,
267+
recursive_call,
268+
missing_function,
269+
not_enough_arguments);
270+
271+
// check that the only no body / missing functions are the cprover builtins
272+
for(const auto &id : no_body)
273+
{
274+
INVARIANT(
275+
library.is_front_end_builtin(id),
276+
"no body for '" + id2string(id) + "' when inlining goto program");
277+
}
278+
279+
for(auto it : missing_function)
280+
{
281+
INVARIANT(
282+
library.is_front_end_builtin(it),
283+
"missing function '" + id2string(it) + "' when inlining goto program");
284+
}
285+
286+
INVARIANT(
287+
recursive_call.size() == 0,
288+
"recursive calls found when inlining goto program");
289+
290+
INVARIANT(
291+
not_enough_arguments.size() == 0,
292+
"not enough arguments when inlining goto program");
293+
}

0 commit comments

Comments
 (0)