Skip to content

Commit 8a32f3f

Browse files
committed
Detect loop locals with goto_rw in DFCC
1 parent 20a1ecf commit 8a32f3f

File tree

5 files changed

+114
-25
lines changed

5 files changed

+114
-25
lines changed

regression/contracts-dfcc/loop_assigns_inference-02/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ void main()
1010
void foo()
1111
{
1212
int *b = malloc(SIZE * sizeof(int));
13+
int *j;
1314
for(unsigned i = 0; i < SIZE; i++)
1415
// clang-format off
1516
__CPROVER_loop_invariant(i <= SIZE)
1617
// clang-format on
1718
{
19+
j = malloc(SIZE * sizeof(int));
1820
b[i] = 1;
21+
free(j);
1922
}
2023
}

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

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -253,26 +253,6 @@ static bool is_assigned(dirtyt &dirty, const irep_idt &ident, assignst assigns)
253253
return false;
254254
}
255255

256-
/// Collect identifiers that are local to this loop only
257-
/// (excluding nested loop).
258-
static std::unordered_set<irep_idt> gen_loop_locals_set(
259-
const dfcc_loop_nesting_grapht &loop_nesting_graph,
260-
const std::size_t loop_id)
261-
{
262-
std::unordered_set<irep_idt> loop_locals;
263-
for(const auto &target : loop_nesting_graph[loop_id].instructions)
264-
{
265-
auto loop_id_opt = dfcc_get_loop_id(target);
266-
if(
267-
target->is_decl() && loop_id_opt.has_value() &&
268-
loop_id_opt.value() == loop_id)
269-
{
270-
loop_locals.insert(target->decl_symbol().get_identifier());
271-
}
272-
}
273-
return loop_locals;
274-
}
275-
276256
/// Compute subset of locals that must be tracked in the loop's write set.
277257
/// A local must be tracked if it is dirty or if it may be assigned by one
278258
/// of the inner loops.
@@ -329,6 +309,7 @@ static struct contract_clausest default_loop_contract_clauses(
329309
const dfcc_loop_nesting_grapht &loop_nesting_graph,
330310
const std::size_t loop_id,
331311
const irep_idt &function_id,
312+
goto_functiont &goto_function,
332313
local_may_aliast &local_may_alias,
333314
const bool check_side_effect,
334315
message_handlert &message_handler,
@@ -381,7 +362,12 @@ static struct contract_clausest default_loop_contract_clauses(
381362
{
382363
// infer assigns clause targets if none given
383364
auto inferred = dfcc_infer_loop_assigns(
384-
local_may_alias, loop.instructions, loop.head->source_location(), ns);
365+
local_may_alias,
366+
goto_function,
367+
loop.instructions,
368+
loop.head->source_location(),
369+
message_handler,
370+
ns);
385371
log.warning() << "No assigns clause provided for loop " << function_id
386372
<< "." << loop.latch->loop_number << " at "
387373
<< loop.head->source_location() << ". The inferred set {";
@@ -416,6 +402,7 @@ static dfcc_loop_infot gen_dfcc_loop_info(
416402
const dfcc_loop_nesting_grapht &loop_nesting_graph,
417403
const std::size_t loop_id,
418404
const irep_idt &function_id,
405+
goto_functiont &goto_function,
419406
const std::map<std::size_t, dfcc_loop_infot> &loop_info_map,
420407
dirtyt &dirty,
421408
local_may_aliast &local_may_alias,
@@ -424,20 +411,25 @@ static dfcc_loop_infot gen_dfcc_loop_info(
424411
dfcc_libraryt &library,
425412
symbol_table_baset &symbol_table)
426413
{
427-
std::unordered_set<irep_idt> loop_locals =
428-
gen_loop_locals_set(loop_nesting_graph, loop_id);
414+
const namespacet ns(symbol_table);
415+
std::unordered_set<irep_idt> loop_locals = gen_loop_locals_set(
416+
function_id,
417+
goto_function,
418+
loop_nesting_graph[loop_id].instructions,
419+
message_handler,
420+
ns);
429421

430422
std::unordered_set<irep_idt> loop_tracked = gen_tracked_set(
431423
loop_nesting_graph.get_predecessors(loop_id),
432424
loop_locals,
433425
dirty,
434426
loop_info_map);
435427

436-
const namespacet ns(symbol_table);
437428
struct contract_clausest contract_clauses = default_loop_contract_clauses(
438429
loop_nesting_graph,
439430
loop_id,
440431
function_id,
432+
goto_function,
441433
local_may_alias,
442434
check_side_effect,
443435
message_handler,
@@ -559,6 +551,7 @@ dfcc_cfg_infot::dfcc_cfg_infot(
559551
loop_nesting_graph,
560552
loop_id,
561553
function_id,
554+
goto_function,
562555
loop_info_map,
563556
dirty,
564557
local_may_alias,

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

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Remi Delmas, [email protected]
1313
#include <util/pointer_expr.h>
1414
#include <util/std_code.h>
1515

16+
#include <analyses/goto_rw.h>
1617
#include <goto-instrument/contracts/utils.h>
1718
#include <goto-instrument/havoc_utils.h>
1819

@@ -46,18 +47,91 @@ depends_on(const exprt &expr, std::unordered_set<irep_idt> identifiers)
4647
return false;
4748
}
4849

50+
/// Collect identifiers that are local to this loop only
51+
/// (excluding nested loop).
52+
/// A target is a loop local if
53+
/// 1. it is declared inside the loop, or
54+
/// 2. there is no write or read of it outside the loop.
55+
std::unordered_set<irep_idt> gen_loop_locals_set(
56+
const irep_idt &function_id,
57+
goto_functiont &goto_function,
58+
const loopt &loop,
59+
message_handlert &message_handler,
60+
const namespacet &ns)
61+
{
62+
std::unordered_set<irep_idt> loop_locals;
63+
std::unordered_set<irep_idt> non_loop_locals;
64+
// Ranges of all variables declared outside the loop.
65+
rw_range_sett decl_rw_range_set(ns, message_handler);
66+
// Ranges of all read/write outside the loop.
67+
rw_range_sett non_loop_rw_range_set(ns, message_handler);
68+
69+
Forall_goto_program_instructions(i_it, goto_function.body)
70+
{
71+
// All variables declared in loops are loop locals.
72+
if(i_it->is_decl() && loop.contains(i_it))
73+
{
74+
loop_locals.insert(i_it->decl_symbol().get_identifier());
75+
}
76+
// Record all other declared variables and their ranges.
77+
else if(i_it->is_decl())
78+
{
79+
goto_rw(function_id, i_it, decl_rw_range_set);
80+
}
81+
// Record all writing/reading outside the loop.
82+
else if(
83+
(i_it->is_assign() || i_it->is_function_call()) && !loop.contains(i_it))
84+
{
85+
goto_rw(function_id, i_it, non_loop_rw_range_set);
86+
}
87+
}
88+
89+
// Check if declared variables are loop locals.
90+
for(const auto &decl_rw : decl_rw_range_set.get_w_set())
91+
{
92+
bool is_loop_local = true;
93+
// No write to the declared variable.
94+
for(const auto &writing_rw : non_loop_rw_range_set.get_w_set())
95+
{
96+
if(decl_rw.first == writing_rw.first)
97+
{
98+
is_loop_local = false;
99+
break;
100+
}
101+
}
102+
103+
// No read to the declared variable.
104+
for(const auto &writing_rw : non_loop_rw_range_set.get_r_set())
105+
{
106+
if(decl_rw.first == writing_rw.first)
107+
{
108+
is_loop_local = false;
109+
break;
110+
}
111+
}
112+
113+
if(is_loop_local)
114+
loop_locals.insert(decl_rw.first);
115+
}
116+
117+
return loop_locals;
118+
}
119+
49120
assignst dfcc_infer_loop_assigns(
50121
const local_may_aliast &local_may_alias,
122+
goto_functiont &goto_function,
51123
const loopt &loop_instructions,
52124
const source_locationt &loop_head_location,
125+
message_handlert &message_handler,
53126
const namespacet &ns)
54127
{
55128
// infer
56129
assignst assigns;
57130
infer_loop_assigns(local_may_alias, loop_instructions, assigns);
58131

59132
// compute locals
60-
std::unordered_set<irep_idt> loop_locals;
133+
std::unordered_set<irep_idt> loop_locals = gen_loop_locals_set(
134+
irep_idt(), goto_function, loop_instructions, message_handler, ns);
61135
for(const auto &target : loop_instructions)
62136
{
63137
if(target->is_decl())

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,25 @@ Author: Remi Delmas, [email protected]
1717
class source_locationt;
1818
class messaget;
1919
class namespacet;
20+
class message_handlert;
2021

2122
// \brief Infer assigns clause targets for a loop from its instructions and a
2223
// may alias analysis at the function level.
2324
assignst dfcc_infer_loop_assigns(
2425
const local_may_aliast &local_may_alias,
26+
goto_functiont &goto_function,
2527
const loopt &loop_instructions,
2628
const source_locationt &loop_head_location,
29+
message_handlert &message_handler,
30+
const namespacet &ns);
31+
32+
/// Collect identifiers that are local to this loop only
33+
/// (excluding nested loop).
34+
std::unordered_set<irep_idt> gen_loop_locals_set(
35+
const irep_idt &function_id,
36+
goto_functiont &goto_function,
37+
const loopt &loop,
38+
message_handlert &message_handler,
2739
const namespacet &ns);
2840

2941
#endif

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ dfcc_instrument_loopt::add_prehead_instructions(
298298
code_function_callt call = library.write_set_create_call(
299299
addr_of_loop_write_set,
300300
from_integer(assigns.size(), size_type()),
301+
from_integer(0, size_type()),
302+
false_exprt(),
303+
false_exprt(),
304+
false_exprt(),
305+
false_exprt(),
306+
true_exprt(),
307+
true_exprt(),
301308
loop_head_location);
302309
pre_loop_head_instrs.add(
303310
goto_programt::make_function_call(call, loop_head_location));

0 commit comments

Comments
 (0)