Skip to content

Commit 09a3899

Browse files
committed
Infer loop assigns for DFCC with functions inlined
1 parent 20a1ecf commit 09a3899

File tree

7 files changed

+179
-12
lines changed

7 files changed

+179
-12
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
struct hole
2+
{
3+
int pos;
4+
};
5+
6+
void set_len(struct hole *h, unsigned long int new_len)
7+
{
8+
h->pos = new_len;
9+
}
10+
11+
int main()
12+
{
13+
int i = 0;
14+
struct hole h;
15+
h.pos = 0;
16+
for(i = 0; i < 5; i++)
17+
// __CPROVER_assigns(h.pos, i)
18+
__CPROVER_loop_invariant(h.pos == i)
19+
{
20+
set_len(&h, h.pos + 1);
21+
}
22+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE dfcc-only
2+
main.c
3+
--dfcc main --apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.loop_invariant_base.\d+\] line \d+ Check invariant before entry for loop .*: SUCCESS$
7+
^\[main.loop_invariant_base.\d+\] line \d+ Check invariant before entry for loop .*: SUCCESS$
8+
^\[main.loop_invariant_step.\d+\] line \d+ Check invariant after step for loop .*: SUCCESS$
9+
^\[main.loop_step_unwinding.\d+\] line \d+ Check step was unwound for loop .*: SUCCESS$
10+
^\[set_len.assigns.\d+\] line \d+ Check that h\-\>pos is assignable: SUCCESS
11+
^VERIFICATION SUCCESSFUL$
12+
--
13+
--
14+
This test checks assigns h->pos is inferred correctly.

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ static struct contract_clausest default_loop_contract_clauses(
329329
const dfcc_loop_nesting_grapht &loop_nesting_graph,
330330
const std::size_t loop_id,
331331
const irep_idt &function_id,
332-
local_may_aliast &local_may_alias,
332+
const assignst &inferred_assigns,
333333
const bool check_side_effect,
334334
message_handlert &message_handler,
335335
const namespacet &ns)
@@ -380,13 +380,11 @@ static struct contract_clausest default_loop_contract_clauses(
380380
else
381381
{
382382
// infer assigns clause targets if none given
383-
auto inferred = dfcc_infer_loop_assigns(
384-
local_may_alias, loop.instructions, loop.head->source_location(), ns);
385383
log.warning() << "No assigns clause provided for loop " << function_id
386384
<< "." << loop.latch->loop_number << " at "
387385
<< loop.head->source_location() << ". The inferred set {";
388386
bool first = true;
389-
for(const auto &expr : inferred)
387+
for(const auto &expr : inferred_assigns)
390388
{
391389
if(!first)
392390
{
@@ -398,7 +396,7 @@ static struct contract_clausest default_loop_contract_clauses(
398396
log.warning() << "} might be incomplete or imprecise, please provide an "
399397
"assigns clause if the analysis fails."
400398
<< messaget::eom;
401-
result.assigns.swap(inferred);
399+
result.assigns = inferred_assigns;
402400
}
403401

404402
if(result.decreases_clauses.empty())
@@ -416,14 +414,16 @@ static dfcc_loop_infot gen_dfcc_loop_info(
416414
const dfcc_loop_nesting_grapht &loop_nesting_graph,
417415
const std::size_t loop_id,
418416
const irep_idt &function_id,
417+
goto_functiont &goto_function,
419418
const std::map<std::size_t, dfcc_loop_infot> &loop_info_map,
420419
dirtyt &dirty,
421-
local_may_aliast &local_may_alias,
420+
const assignst &inferred_assigns,
422421
const bool check_side_effect,
423422
message_handlert &message_handler,
424423
dfcc_libraryt &library,
425424
symbol_table_baset &symbol_table)
426425
{
426+
const namespacet ns(symbol_table);
427427
std::unordered_set<irep_idt> loop_locals =
428428
gen_loop_locals_set(loop_nesting_graph, loop_id);
429429

@@ -433,12 +433,11 @@ static dfcc_loop_infot gen_dfcc_loop_info(
433433
dirty,
434434
loop_info_map);
435435

436-
const namespacet ns(symbol_table);
437436
struct contract_clausest contract_clauses = default_loop_contract_clauses(
438437
loop_nesting_graph,
439438
loop_id,
440439
function_id,
441-
local_may_alias,
440+
inferred_assigns,
442441
check_side_effect,
443442
message_handler,
444443
ns);
@@ -489,6 +488,7 @@ static dfcc_loop_infot gen_dfcc_loop_info(
489488
}
490489

491490
dfcc_cfg_infot::dfcc_cfg_infot(
491+
goto_modelt &goto_model,
492492
const irep_idt &function_id,
493493
goto_functiont &goto_function,
494494
const exprt &top_level_write_set,
@@ -507,6 +507,9 @@ dfcc_cfg_infot::dfcc_cfg_infot(
507507
// Clean up possible fake loops that are due to do { ... } while(0);
508508
simplify_gotos(goto_program, ns);
509509

510+
// From loop number to the inferred loop assigns.
511+
std::map<std::size_t, assignst> inferred_loop_assigns_map;
512+
510513
if(loop_contract_config.apply_loop_contracts)
511514
{
512515
messaget log(message_handler);
@@ -531,6 +534,14 @@ dfcc_cfg_infot::dfcc_cfg_infot(
531534
{
532535
topsorted_loops.push_back(idx);
533536
}
537+
538+
// We infer loop assigns for all loops in the function.
539+
dfcc_infer_loop_assigns_for_function(
540+
inferred_loop_assigns_map,
541+
goto_model.goto_functions,
542+
goto_function,
543+
message_handler,
544+
ns);
534545
}
535546

536547
// At this point, either loop contracts were activated and the loop nesting
@@ -549,19 +560,21 @@ dfcc_cfg_infot::dfcc_cfg_infot(
549560

550561
// generate dfcc_cfg_loop_info for loops and add to loop_info_map
551562
dirtyt dirty(goto_function);
552-
local_may_aliast local_may_alias(goto_function);
553563

554564
for(const auto &loop_id : topsorted_loops)
555565
{
566+
auto inferred_loop_assigns =
567+
inferred_loop_assigns_map[loop_nesting_graph[loop_id].latch->loop_number];
556568
loop_info_map.insert(
557569
{loop_id,
558570
gen_dfcc_loop_info(
559571
loop_nesting_graph,
560572
loop_id,
561573
function_id,
574+
goto_function,
562575
loop_info_map,
563576
dirty,
564-
local_may_alias,
577+
inferred_loop_assigns,
565578
loop_contract_config.check_side_effect,
566579
message_handler,
567580
library,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Date: March 2023
2121
#include <util/std_expr.h>
2222
#include <util/symbol_table.h>
2323

24+
#include <goto-programs/goto_model.h>
2425
#include <goto-programs/goto_program.h>
2526

2627
#include <goto-instrument/contracts/loop_contract_config.h>
@@ -242,6 +243,7 @@ class dfcc_cfg_infot
242243
{
243244
public:
244245
dfcc_cfg_infot(
246+
goto_modelt &goto_model,
245247
const irep_idt &function_id,
246248
goto_functiont &goto_function,
247249
const exprt &top_level_write_set,

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

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ Author: Remi Delmas, delmasrd@amazon.com
1313
#include <util/pointer_expr.h>
1414
#include <util/std_code.h>
1515

16+
#include <goto-programs/goto_inline.h>
17+
18+
#include <analyses/goto_rw.h>
1619
#include <goto-instrument/contracts/utils.h>
1720
#include <goto-instrument/havoc_utils.h>
1821

22+
#include "dfcc_loop_nesting_graph.h"
1923
#include "dfcc_root_object.h"
2024

2125
/// Builds a call expression `object_whole(expr)`
@@ -46,10 +50,12 @@ depends_on(const exprt &expr, std::unordered_set<irep_idt> identifiers)
4650
return false;
4751
}
4852

49-
assignst dfcc_infer_loop_assigns(
53+
assignst dfcc_infer_loop_assigns_for_loop(
5054
const local_may_aliast &local_may_alias,
55+
goto_functiont &goto_function,
5156
const loopt &loop_instructions,
5257
const source_locationt &loop_head_location,
58+
message_handlert &message_handler,
5359
const namespacet &ns)
5460
{
5561
// infer
@@ -105,3 +111,99 @@ assignst dfcc_infer_loop_assigns(
105111

106112
return result;
107113
}
114+
115+
/// Remove assignments to `__CPROVER_dead_object` to avoid aliasing all targets
116+
/// that are assigned to `__CPROVER_dead_object`.
117+
static void remove_dead_object_assignment(goto_functiont &goto_function)
118+
{
119+
Forall_goto_program_instructions(i_it, goto_function.body)
120+
{
121+
if(i_it->is_assign())
122+
{
123+
auto &lhs = i_it->assign_lhs();
124+
125+
if(
126+
lhs.id() == ID_symbol &&
127+
to_symbol_expr(lhs).get_identifier() == CPROVER_PREFIX "dead_object")
128+
{
129+
i_it->turn_into_skip();
130+
}
131+
}
132+
}
133+
}
134+
135+
void dfcc_infer_loop_assigns_for_function(
136+
std::map<std::size_t, assignst> &inferred_loop_assigns_map,
137+
goto_functionst &goto_functions,
138+
goto_functiont &goto_function,
139+
message_handlert &message_handler,
140+
const namespacet &ns)
141+
{
142+
messaget log(message_handler);
143+
144+
// We infer loop assigns based on the copy of `goto_function`.
145+
goto_functiont goto_function_copy;
146+
goto_function_copy.copy_from(goto_function);
147+
148+
// Map from targett in `goto_function_copy` to loop number.
149+
std::
150+
unordered_map<goto_programt::const_targett, std::size_t, const_target_hash>
151+
loop_number_map;
152+
153+
// Build the loop id map before inlining attempt.
154+
const auto loop_nesting_graph =
155+
build_loop_nesting_graph(goto_function_copy.body);
156+
{
157+
auto topsorted = loop_nesting_graph.topsort();
158+
// skip function without loop.
159+
if(topsorted.empty())
160+
return;
161+
162+
for(const auto id : topsorted)
163+
{
164+
loop_number_map.emplace(
165+
loop_nesting_graph[id].head, loop_nesting_graph[id].latch->loop_number);
166+
}
167+
}
168+
169+
// We avoid inlining `malloc` and `free` whose variables should not be assigns.
170+
auto malloc_body = goto_functions.function_map.extract(irep_idt("malloc"));
171+
auto free_body = goto_functions.function_map.extract(irep_idt("free"));
172+
173+
// Inline all function calls in goto_function_copy.
174+
goto_program_inline(
175+
goto_functions, goto_function_copy.body, ns, log.get_message_handler());
176+
goto_function_copy.body.update();
177+
// Build the loop graph after inlining.
178+
const auto inlined_loop_nesting_graph =
179+
build_loop_nesting_graph(goto_function_copy.body);
180+
181+
// Alias analysis.
182+
remove_dead_object_assignment(goto_function_copy);
183+
local_may_aliast local_may_alias(goto_function_copy);
184+
185+
auto inlined_topsorted = inlined_loop_nesting_graph.topsort();
186+
187+
for(const auto inlined_id : inlined_topsorted)
188+
{
189+
// We only infer loop assigns for loops in the original function.
190+
if(
191+
loop_number_map.find(inlined_loop_nesting_graph[inlined_id].head) !=
192+
loop_number_map.end())
193+
{
194+
const auto loop_number =
195+
loop_number_map[inlined_loop_nesting_graph[inlined_id].head];
196+
const auto inlined_loop = inlined_loop_nesting_graph[inlined_id];
197+
198+
inferred_loop_assigns_map[loop_number] = dfcc_infer_loop_assigns_for_loop(
199+
local_may_alias,
200+
goto_function_copy,
201+
inlined_loop.instructions,
202+
inlined_loop.head->source_location(),
203+
message_handler,
204+
ns);
205+
}
206+
}
207+
goto_functions.function_map.insert(std::move(malloc_body));
208+
goto_functions.function_map.insert(std::move(free_body));
209+
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,25 @@ Author: Remi Delmas, delmasrd@amazon.com
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.
23-
assignst dfcc_infer_loop_assigns(
24+
assignst dfcc_infer_loop_assigns_for_loop(
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+
// \brief Infer assigns clause targets for loops in `goto_function` from their
33+
// instructions and a may alias analysis at the function level (with inlining).
34+
void dfcc_infer_loop_assigns_for_function(
35+
std::map<std::size_t, assignst> &inferred_loop_assigns_map,
36+
goto_functionst &goto_functions,
37+
goto_functiont &goto_function,
38+
message_handlert &message_handler,
2739
const namespacet &ns);
2840

2941
#endif

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ void dfcc_instrumentt::instrument_goto_program(
399399

400400
// build control flow graph information
401401
dfcc_cfg_infot cfg_info(
402+
goto_model,
402403
function_id,
403404
goto_function,
404405
write_set,
@@ -448,6 +449,7 @@ void dfcc_instrumentt::instrument_goto_function(
448449

449450
// build control flow graph information
450451
dfcc_cfg_infot cfg_info(
452+
goto_model,
451453
function_id,
452454
goto_function,
453455
write_set,

0 commit comments

Comments
 (0)