Skip to content

Commit 6f175c5

Browse files
author
Remi Delmas
committed
CONTRACTS: class that applies loop contract transformations
1 parent 170daa6 commit 6f175c5

File tree

2 files changed

+67
-49
lines changed

2 files changed

+67
-49
lines changed

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

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dfcc_instrument_loopt::dfcc_instrument_loopt(
3232
dfcc_spec_functionst &spec_functions,
3333
dfcc_contract_clauses_codegent &contract_clauses_codegen)
3434
: goto_model(goto_model),
35+
message_handler(message_handler),
3536
log(message_handler),
3637
utils(utils),
3738
library(library),
@@ -88,7 +89,7 @@ void dfcc_instrument_loopt::operator()(
8889
.symbol_expr();
8990

9091
// Temporary variables for storing the multidimensional decreases clause
91-
// at the start of and end of a loop body.
92+
// at the start of and end of a loop body
9293
exprt::operandst decreases_clauses = loop.decreases;
9394
std::vector<symbol_exprt> old_decreases_vars, new_decreases_vars;
9495
for(const auto &clause : decreases_clauses)
@@ -105,10 +106,10 @@ void dfcc_instrument_loopt::operator()(
105106
new_decreases_vars.push_back(new_decreases_var);
106107
}
107108

108-
// Convert the assigns clause to the required type.
109+
// convert the assigns clause to the required type
109110
exprt::operandst assigns(loop.assigns.begin(), loop.assigns.end());
110111

111-
// Add local statics to the assigns clause.
112+
// add local statics to the assigns clause
112113
for(auto &local_static : local_statics)
113114
{
114115
assigns.push_back(local_static);
@@ -128,24 +129,26 @@ void dfcc_instrument_loopt::operator()(
128129
contract_clauses_codegen.gen_spec_assigns_instructions(
129130
language_mode, assigns, assigns_instrs);
130131

132+
const symbol_exprt &addr_of_loop_write_set = loop.addr_of_write_set_var;
131133
spec_functions.generate_havoc_instructions(
132134
function_id,
133135
language_mode,
134136
symbol_table.get_writeable_ref(function_id).module,
135137
assigns_instrs,
136-
loop.addr_of_write_set_var,
138+
addr_of_loop_write_set,
137139
havoc_instrs,
138140
nof_targets);
139141
spec_functions.to_spec_assigns_instructions(
140-
loop.addr_of_write_set_var, language_mode, assigns_instrs, nof_targets);
142+
addr_of_loop_write_set, language_mode, assigns_instrs, nof_targets);
141143
write_set_populate_instrs.copy_from(assigns_instrs);
142144

143-
// Replace bound variables by fresh instances in quantified formulas.
145+
// replace bound variables by fresh instances in quantified formulas
144146
exprt invariant = loop.invariant;
145147
if(has_subexpr(invariant, ID_exists) || has_subexpr(invariant, ID_forall))
146148
add_quantified_variable(symbol_table, invariant, language_mode);
147149

148150
// ---------- Add instrumented instructions ----------
151+
const symbol_exprt &loop_write_set = loop.write_set_var;
149152
goto_programt::targett loop_latch =
150153
loop.find_latch(goto_function.body).value();
151154

@@ -158,8 +161,8 @@ void dfcc_instrument_loopt::operator()(
158161
write_set_populate_instrs,
159162
invariant,
160163
assigns,
161-
loop.write_set_var,
162-
loop.addr_of_write_set_var,
164+
loop_write_set,
165+
addr_of_loop_write_set,
163166
entered_loop,
164167
initial_invariant,
165168
in_base_case,
@@ -176,7 +179,7 @@ void dfcc_instrument_loopt::operator()(
176179
havoc_instrs,
177180
invariant,
178181
decreases_clauses,
179-
loop.addr_of_write_set_var,
182+
addr_of_loop_write_set,
180183
outer_write_set,
181184
initial_invariant,
182185
in_base_case,
@@ -204,8 +207,9 @@ void dfcc_instrument_loopt::operator()(
204207
cbmc_loop_id,
205208
goto_function,
206209
head,
207-
loop.write_set_var,
208-
loop.addr_of_write_set_var,
210+
decreases_clauses,
211+
loop_write_set,
212+
addr_of_loop_write_set,
209213
history_var_map,
210214
entered_loop,
211215
initial_invariant,
@@ -234,6 +238,7 @@ std::map<exprt, exprt> dfcc_instrument_loopt::add_prehead_instructions(
234238
{
235239
auto loop_head_location(loop_head->source_location());
236240
dfcc_remove_loop_tags(loop_head_location);
241+
goto_convertt converter(symbol_table, message_handler);
237242

238243
// ```
239244
// ... preamble ...
@@ -278,7 +283,6 @@ std::map<exprt, exprt> dfcc_instrument_loopt::add_prehead_instructions(
278283

279284
// initial_invariant = <loop_invariant>;
280285
{
281-
goto_convertt converter(symbol_table, log.get_message_handler());
282286
// Create a snapshot of the invariant so that we can check the base case,
283287
// if the loop is not vacuous and must be abstracted with contracts.
284288
pre_loop_head_instrs.add(
@@ -304,7 +308,7 @@ std::map<exprt, exprt> dfcc_instrument_loopt::add_prehead_instructions(
304308
}
305309

306310
{
307-
// Create and populate the write set.
311+
// create and populate the write set
308312
// DECL loop_write_set
309313
// DECL addr_of_loop_write_set
310314
// ASSIGN write_set_ptr := address_of(write_set)
@@ -367,6 +371,7 @@ dfcc_instrument_loopt::add_step_instructions(
367371
{
368372
auto loop_head_location(loop_head->source_location());
369373
dfcc_remove_loop_tags(loop_head_location);
374+
goto_convertt converter(symbol_table, message_handler);
370375

371376
// ```
372377
// STEP: // Loop step block: havoc the loop state
@@ -393,20 +398,21 @@ dfcc_instrument_loopt::add_step_instructions(
393398

394399
{
395400
// If we jump here, then the loop runs at least once,
396-
// so add the base case assertion: `assert(initial_invariant)`.
401+
// so add the base case assertion: `assert(initial_invariant)`
397402
source_locationt check_location(loop_head_location);
398403
check_location.set_property_class("loop_invariant_base");
399404
check_location.set_comment(
400405
"Check invariant before entry for loop " +
401406
id2string(check_location.get_function()) + "." +
402407
std::to_string(cbmc_loop_id));
403-
step_instrs.add(
404-
goto_programt::make_assertion(initial_invariant, check_location));
408+
code_assertt assertion{initial_invariant};
409+
assertion.add_source_location() = check_location;
410+
converter.goto_convert(assertion, step_instrs, language_mode);
405411
}
406412

407413
{
408-
// Check assigns clause inclusion with parent write set
409-
// skip the check when if w_parent is NULL.
414+
// check assigns clause inclusion with parent write set
415+
// skip the check when if w_parent is NULL
410416
auto goto_instruction = step_instrs.add(goto_programt::make_incomplete_goto(
411417
equal_exprt(
412418
outer_write_set,
@@ -462,24 +468,26 @@ dfcc_instrument_loopt::add_step_instructions(
462468
goto_programt::make_assignment(in_loop_havoc_block, false_exprt{}));
463469
}
464470

465-
goto_convertt converter(symbol_table, log.get_message_handler());
466471
{
467-
// Assume the loop invariant after havocing the state.
472+
// Assume the loop invariant after havocing the state
468473
code_assumet assumption{invariant};
469474
assumption.add_source_location() = loop_head_location;
470475
converter.goto_convert(assumption, step_instrs, language_mode);
471476
}
472477

473478
{
474479
// Generate assignments to store the multidimensional decreases clause's
475-
// value just before the loop_head.
476-
for(size_t i = 0; i < old_decreases_vars.size(); i++)
480+
// value just before the loop_head
481+
if(!decreases_clauses.empty())
477482
{
478-
code_assignt old_decreases_assignment{
479-
old_decreases_vars[i], decreases_clauses[i]};
480-
old_decreases_assignment.add_source_location() = loop_head_location;
481-
converter.goto_convert(
482-
old_decreases_assignment, step_instrs, language_mode);
483+
for(size_t i = 0; i < old_decreases_vars.size(); i++)
484+
{
485+
code_assignt old_decreases_assignment{
486+
old_decreases_vars[i], decreases_clauses[i]};
487+
old_decreases_assignment.add_source_location() = loop_head_location;
488+
converter.goto_convert(
489+
old_decreases_assignment, step_instrs, language_mode);
490+
}
483491
}
484492
}
485493

@@ -506,6 +514,7 @@ void dfcc_instrument_loopt::add_body_instructions(
506514
{
507515
auto loop_head_location(loop_head->source_location());
508516
dfcc_remove_loop_tags(loop_head_location);
517+
goto_convertt converter(symbol_table, message_handler);
509518

510519
// HEAD: // Loop body block
511520
// ... eval guard ...
@@ -523,7 +532,7 @@ void dfcc_instrument_loopt::add_body_instructions(
523532
goto_programt pre_loop_latch_instrs;
524533

525534
{
526-
// Record that we entered the loop with `entered_loop = true`.
535+
// Record that we entered the loop with `entered_loop = true`
527536
pre_loop_latch_instrs.add(
528537
goto_programt::make_assignment(entered_loop, true_exprt{}));
529538
}
@@ -535,7 +544,6 @@ void dfcc_instrument_loopt::add_body_instructions(
535544
step_case_target, in_base_case, loop_head_location));
536545
}
537546

538-
goto_convertt converter(symbol_table, log.get_message_handler());
539547
{
540548
// Because of the unconditional jump above the following code is only
541549
// reachable in the step case. Generate the inductive invariant check
@@ -553,7 +561,7 @@ void dfcc_instrument_loopt::add_body_instructions(
553561

554562
{
555563
// Generate assignments to store the multidimensional decreases clause's
556-
// value after one iteration of the loop.
564+
// value after one iteration of the loop
557565
if(!decreases_clauses.empty())
558566
{
559567
for(size_t i = 0; i < new_decreases_vars.size(); i++)
@@ -573,12 +581,14 @@ void dfcc_instrument_loopt::add_body_instructions(
573581
"Check variant decreases after step for loop " +
574582
id2string(check_location.get_function()) + "." +
575583
std::to_string(cbmc_loop_id));
576-
pre_loop_latch_instrs.add(goto_programt::make_assertion(
584+
code_assertt monotonic_decreasing_assertion{
577585
generate_lexicographic_less_than_check(
578-
new_decreases_vars, old_decreases_vars),
579-
check_location));
586+
new_decreases_vars, old_decreases_vars)};
587+
monotonic_decreasing_assertion.add_source_location() = check_location;
588+
converter.goto_convert(
589+
monotonic_decreasing_assertion, pre_loop_latch_instrs, language_mode);
580590

581-
// Discard the temporary variables that store decreases clause's value.
591+
// Discard the temporary variables that store decreases clause's value
582592
for(size_t i = 0; i < old_decreases_vars.size(); i++)
583593
{
584594
pre_loop_latch_instrs.add(
@@ -593,7 +603,7 @@ void dfcc_instrument_loopt::add_body_instructions(
593603
goto_function.body, loop_latch, pre_loop_latch_instrs);
594604

595605
{
596-
// Change the back edge into assume(false) or assume(!guard).
606+
// change the back edge into assume(false) or assume(!guard)
597607
loop_latch->turn_into_assume();
598608
loop_latch->condition_nonconst() = boolean_negate(loop_latch->condition());
599609
}
@@ -604,6 +614,7 @@ void dfcc_instrument_loopt::add_exit_instructions(
604614
const std::size_t cbmc_loop_id,
605615
goto_functionst::goto_functiont &goto_function,
606616
goto_programt::targett loop_head,
617+
const exprt::operandst &decreases_clauses,
607618
const symbol_exprt &loop_write_set,
608619
const symbol_exprt &addr_of_loop_write_set,
609620
const std::map<exprt, exprt> &history_var_map,
@@ -613,7 +624,7 @@ void dfcc_instrument_loopt::add_exit_instructions(
613624
const std::vector<symbol_exprt> &old_decreases_vars,
614625
const std::vector<symbol_exprt> &new_decreases_vars)
615626
{
616-
// Collect all exit targets of the loop.
627+
// collect all exit targets of the loop
617628
std::set<goto_programt::targett> exit_targets;
618629

619630
for(goto_programt::instructiont::targett target =
@@ -632,7 +643,7 @@ void dfcc_instrument_loopt::add_exit_instructions(
632643
exit_targets.insert(exit_target);
633644
}
634645

635-
// For each exit target of the loop, insert a code block:
646+
// For each exit target of the loop, insert a code block :
636647
// ```
637648
// EXIT:
638649
// // check that step case was checked if loop can run once
@@ -648,8 +659,8 @@ void dfcc_instrument_loopt::add_exit_instructions(
648659
{
649660
goto_programt loop_exit_program;
650661

651-
// Use the head location for this check as well so that all checks related
652-
// to a given loop are presented as coming from the loop head.
662+
// use the head location for this check as well so that all checks related
663+
// to a given loop are presented as coming from the loop head
653664
source_locationt check_location = loop_head->source_location();
654665
check_location.set_property_class("loop_step_unwinding");
655666
check_location.set_comment(
@@ -660,7 +671,7 @@ void dfcc_instrument_loopt::add_exit_instructions(
660671
or_exprt{not_exprt{entered_loop}, not_exprt{in_base_case}},
661672
check_location));
662673

663-
// Mark instrumentation variables as going out of scope.
674+
// Kill instrumentation variables.
664675
source_locationt exit_location = exit_target->source_location();
665676
loop_exit_program.add(
666677
goto_programt::make_dead(in_base_case, exit_location));
@@ -669,31 +680,34 @@ void dfcc_instrument_loopt::add_exit_instructions(
669680
loop_exit_program.add(
670681
goto_programt::make_dead(initial_invariant, exit_location));
671682

672-
// Release the write set resources.
683+
// Release the write set resources
673684
loop_exit_program.add(goto_programt::make_function_call(
674685
library.write_set_release_call(addr_of_loop_write_set, exit_location),
675686
exit_location));
676687

677-
// Mark write set as going out of scope.
688+
// Kill write set
678689
loop_exit_program.add(
679690
goto_programt::make_dead(to_symbol_expr(loop_write_set), exit_location));
680691
loop_exit_program.add(goto_programt::make_dead(
681692
to_symbol_expr(addr_of_loop_write_set), exit_location));
682693

683-
// Mark history variables as going out of scope.
694+
// Kill history variables
684695
for(const auto &v : history_var_map)
685696
{
686697
loop_exit_program.add(
687698
goto_programt::make_dead(to_symbol_expr(v.second), exit_location));
688699
}
689700

690-
// Mark decreases clause snapshots as gong out of scope.
691-
for(size_t i = 0; i < old_decreases_vars.size(); i++)
701+
if(!decreases_clauses.empty())
692702
{
693-
loop_exit_program.add(
694-
goto_programt::make_dead(old_decreases_vars[i], exit_location));
695-
loop_exit_program.add(
696-
goto_programt::make_dead(new_decreases_vars[i], exit_location));
703+
// Kill decreases clause snapshots
704+
for(size_t i = 0; i < old_decreases_vars.size(); i++)
705+
{
706+
loop_exit_program.add(
707+
goto_programt::make_dead(old_decreases_vars[i], exit_location));
708+
loop_exit_program.add(
709+
goto_programt::make_dead(new_decreases_vars[i], exit_location));
710+
}
697711
}
698712

699713
// Insert the exit block, preserving the loop end target.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Date: April 2023
1717
#ifndef CPROVER_GOTO_INSTRUMENT_CONTRACTS_DYNAMIC_FRAMES_DFCC_INSTRUMENT_LOOP_H
1818
#define CPROVER_GOTO_INSTRUMENT_CONTRACTS_DYNAMIC_FRAMES_DFCC_INSTRUMENT_LOOP_H
1919

20+
#include <util/irep.h>
2021
#include <util/message.h>
2122

2223
#include <goto-programs/goto_model.h>
@@ -150,6 +151,7 @@ class dfcc_instrument_loopt
150151
// keeps track of the maximum number of assigns clause targets
151152
std::size_t max_assigns_clause_size = 0;
152153
goto_modelt &goto_model;
154+
message_handlert &message_handler;
153155
messaget log;
154156
dfcc_utilst &utils;
155157
dfcc_libraryt &library;
@@ -333,6 +335,7 @@ class dfcc_instrument_loopt
333335
/// \param[in] cbmc_loop_id Id assigned to the loop by CBMC's loop numbering.
334336
/// \param[inout] goto_function The function containing the loop.
335337
/// \param[in] loop_head Head node of the loop.
338+
/// \param[in] decreases_clauses Decreases clause.
336339
/// \param[in] loop_write_set Stack allocated loop write set variable.
337340
/// \param[in] addr_of_loop_write_set Loop write set pointer variable.
338341
/// \param[in] history_var_map Map storing history variables of the loop.
@@ -346,6 +349,7 @@ class dfcc_instrument_loopt
346349
const std::size_t cbmc_loop_id,
347350
goto_functionst::goto_functiont &goto_function,
348351
goto_programt::targett loop_head,
352+
const exprt::operandst &decreases_clauses,
349353
const symbol_exprt &loop_write_set,
350354
const symbol_exprt &addr_of_loop_write_set,
351355
const std::map<exprt, exprt> &history_var_map,

0 commit comments

Comments
 (0)