Skip to content

Commit 948994a

Browse files
committed
loop-invariant-synthesizer interface
1 parent adf0a44 commit 948994a

13 files changed

+417
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
unsigned int size;
6+
__CPROVER_assume(size < 50);
7+
int *p = malloc(size * 4);
8+
int result = 0;
9+
10+
for(unsigned int i = 0; i < size; i++)
11+
{
12+
result += p[i];
13+
}
14+
15+
for(unsigned int i = 0; i < size; i++)
16+
// clang-format off
17+
__CPROVER_loop_invariant(1 == 1)
18+
// clang-format on
19+
{
20+
result += p[i];
21+
}
22+
23+
for(unsigned int i = 0; i < size; i++)
24+
{
25+
result += p[i];
26+
}
27+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CORE
2+
main.c
3+
--synthesize-loop-invariants --apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main\.\d+\] line 10 Check loop invariant before entry: SUCCESS$
7+
^\[main\.\d+\] line 10 Check that loop invariant is preserved: SUCCESS$
8+
^\[main\.\d+\] line 15 Check loop invariant before entry: SUCCESS$
9+
^\[main\.\d+\] line 15 Check that loop invariant is preserved: SUCCESS$
10+
^\[main\.\d+\] line 23 Check loop invariant before entry: SUCCESS$
11+
^\[main\.\d+\] line 23 Check that loop invariant is preserved: SUCCESS$
12+
^VERIFICATION SUCCESSFUL$
13+
--
14+
^warning: ignoring
15+
--
16+
This test case checks if invariants are generated for unannotated loops.

src/goto-instrument/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ SRC = accelerate/accelerate.cpp \
6868
source_lines.cpp \
6969
splice_call.cpp \
7070
stack_depth.cpp \
71+
synthesizer/enumerative_loop_invariant_synthesizer.cpp \
72+
synthesizer/synthesizer_utils.cpp \
7173
thread_instrumentation.cpp \
7274
undefined_functions.cpp \
7375
uninitialized.cpp \

src/goto-instrument/goto_instrument_parse_options.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,17 @@ void goto_instrument_parse_optionst::instrument_goto_program()
11471147
goto_model.goto_functions.update();
11481148
}
11491149

1150+
if(cmdline.isset("synthesize-loop-invariants"))
1151+
{
1152+
log.warning() << "Loop invariant synthesizer is still work in progress. "
1153+
"It only generates TRUE as invariants."
1154+
<< messaget::eom;
1155+
1156+
// Synthesize loop invariants and annotate them into `goto_model`
1157+
enumerative_loop_invariant_synthesizert synthesizer(goto_model, log);
1158+
annotate_invariants(synthesizer.synthesize_all(), goto_model, log);
1159+
}
1160+
11501161
if(
11511162
cmdline.isset(FLAG_LOOP_CONTRACTS) || cmdline.isset(FLAG_REPLACE_CALL) ||
11521163
cmdline.isset(FLAG_ENFORCE_CONTRACT))
@@ -1897,6 +1908,7 @@ void goto_instrument_parse_optionst::help()
18971908
"\n"
18981909
"Code contracts:\n"
18991910
HELP_LOOP_CONTRACTS
1911+
HELP_LOOP_INVARIANT_SYNTHESIZER
19001912
HELP_REPLACE_CALL
19011913
HELP_ENFORCE_CONTRACT
19021914
"\n"

src/goto-instrument/goto_instrument_parse_options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Author: Daniel Kroening, [email protected]
4141
#include "unwindset.h"
4242

4343
#include "contracts/contracts.h"
44+
#include "synthesizer/enumerative_loop_invariant_synthesizer.h"
4445
#include "wmm/weak_memory.h"
4546

4647
// clang-format off
@@ -116,6 +117,7 @@ Author: Daniel Kroening, [email protected]
116117
OPT_NONDET_VOLATILE \
117118
"(ensure-one-backedge-per-target)" \
118119
OPT_CONFIG_LIBRARY \
120+
OPT_SYNTHESIZE_LOOP_INVARIANTS \
119121
// empty last line
120122

121123
// clang-format on

src/goto-instrument/module_dependencies.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ langapi # should go away
1010
linking
1111
pointer-analysis
1212
solvers
13+
synthesizer
1314
util
1415
wmm
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*******************************************************************\
2+
3+
Module: Enumerative Loop Invariant Synthesizer
4+
5+
Author: Qinheping Hu
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Enumerative Loop Invariant Synthesizer
11+
12+
#include "enumerative_loop_invariant_synthesizer.h"
13+
14+
void enumerative_loop_invariant_synthesizert::init_candidates()
15+
{
16+
for(auto &function_p : goto_model.goto_functions.function_map)
17+
{
18+
natural_loopst natural_loops;
19+
natural_loops(function_p.second.body);
20+
21+
// Initialize invariants for unannotated loops as true
22+
for(const auto &loop_head_and_content : natural_loops.loop_map)
23+
{
24+
goto_programt::const_targett loop_end =
25+
get_loop_end_from_loop_head_and_content(
26+
loop_head_and_content.first, loop_head_and_content.second);
27+
28+
loop_idt new_id(function_p.first, loop_end->loop_number);
29+
30+
// we only synthesize invariants for unannotated loops
31+
if(loop_end->condition().find(ID_C_spec_loop_invariant).is_nil())
32+
{
33+
invariant_candiate_map[new_id] = true_exprt();
34+
}
35+
}
36+
}
37+
}
38+
39+
invariant_mapt enumerative_loop_invariant_synthesizert::synthesize_all()
40+
{
41+
init_candidates();
42+
43+
// Now this method only generate true for all unnotated loops.
44+
// The implementation will be added later.
45+
return invariant_candiate_map;
46+
}
47+
48+
exprt enumerative_loop_invariant_synthesizert::synthesize(loop_idt loop_id)
49+
{
50+
return true_exprt();
51+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*******************************************************************\
2+
3+
Module: Enumerative Loop Invariant Synthesizer
4+
5+
Author: Qinheping Hu
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Enumerative Loop Invariant Synthesizer
11+
12+
// NOLINTNEXTLINE(whitespace/line_length)
13+
#ifndef CPROVER_GOTO_INSTRUMENT_SYNTHESIZER_ENUMERATIVE_LOOP_INVARIANT_SYNTHESIZER_H
14+
// NOLINTNEXTLINE(whitespace/line_length)
15+
#define CPROVER_GOTO_INSTRUMENT_SYNTHESIZER_ENUMERATIVE_LOOP_INVARIANT_SYNTHESIZER_H
16+
17+
#include <goto-programs/goto_model.h>
18+
19+
#include "loop_invariant_synthesizer_base.h"
20+
21+
class messaget;
22+
23+
/// Enumerative loop invariant synthesizers.
24+
/// It handles `goto_model` containing only checks instrumented by
25+
/// `goto-instrument` with the `--pointer-check` flag.
26+
class enumerative_loop_invariant_synthesizert
27+
: public loop_invariant_synthesizer_baset
28+
{
29+
public:
30+
enumerative_loop_invariant_synthesizert(
31+
const goto_modelt &goto_model,
32+
messaget &log)
33+
: loop_invariant_synthesizer_baset(goto_model, log)
34+
{
35+
}
36+
37+
invariant_mapt synthesize_all() override;
38+
exprt synthesize(loop_idt loop_id) override;
39+
40+
private:
41+
/// Initialize invariants as true for all unannotated loops.
42+
void init_candidates();
43+
44+
invariant_mapt invariant_candiate_map;
45+
};
46+
47+
// NOLINTNEXTLINE(whitespace/line_length)
48+
#endif // CPROVER_GOTO_INSTRUMENT_SYNTHESIZER_ENUMERATIVE_LOOP_INVARIANT_SYNTHESIZER_H
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*******************************************************************\
2+
3+
Module: Loop Invariant Synthesizer Interface
4+
5+
Author: Qinheping Hu
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Loop Invariant Synthesizer Interface
11+
12+
#ifndef CPROVER_GOTO_INSTRUMENT_SYNTHESIZER_LOOP_INVARIANT_SYNTHESIZER_BASE_H
13+
#define CPROVER_GOTO_INSTRUMENT_SYNTHESIZER_LOOP_INVARIANT_SYNTHESIZER_BASE_H
14+
15+
#include <goto-programs/goto_model.h>
16+
17+
#include "synthesizer_utils.h"
18+
19+
#define OPT_SYNTHESIZE_LOOP_INVARIANTS "(synthesize-loop-invariants)"
20+
#define HELP_LOOP_INVARIANT_SYNTHESIZER \
21+
" --synthesize-loop-invariants\n" \
22+
" synthesize and apply loop invariants\n"
23+
24+
class messaget;
25+
26+
/// A base class for loop invariant synthesizers.
27+
/// Provides a method for synthesizing loop invariants in a given `goto_model`.
28+
///
29+
/// Derived class should clarify what types of `goto_model` they targets, e.g.,
30+
/// a `goto_model` contains only memory safety checks, or a `goto_model`
31+
/// contains both memory safety checks and correctness checks.
32+
class loop_invariant_synthesizer_baset
33+
{
34+
public:
35+
loop_invariant_synthesizer_baset(const goto_modelt &goto_model, messaget &log)
36+
: goto_model(goto_model), log(log)
37+
{
38+
}
39+
virtual ~loop_invariant_synthesizer_baset() = default;
40+
41+
/// Synthesize loop invariants with which all checks in `goto_model`
42+
/// succeed. The result is a map from `loop_idt` ids of loops to `exprt`
43+
/// the goto-expression representation of synthesized invariants.
44+
virtual invariant_mapt synthesize_all() = 0;
45+
46+
/// Synthesize loop invariant for a specified loop in the `goto_model`
47+
virtual exprt synthesize(loop_idt) = 0;
48+
49+
protected:
50+
const goto_modelt &goto_model;
51+
messaget &log;
52+
};
53+
54+
#endif // CPROVER_GOTO_INSTRUMENT_SYNTHESIZER_LOOP_INVARIANT_SYNTHESIZER_BASE_H
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
analyses
2+
ansi-c
3+
goto-instrument/contracts
4+
goto-instrument/synthesizer
5+
goto-instrument
6+
goto-programs
7+
langapi
8+
util
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*******************************************************************\
2+
3+
Module: Utility functions for loop invariant synthesizer.
4+
5+
Author: Qinheping Hu
6+
7+
\*******************************************************************/
8+
9+
#include "synthesizer_utils.h"
10+
11+
goto_programt::const_targett get_loop_end_from_loop_head_and_content(
12+
const goto_programt::const_targett &loop_head,
13+
const loop_templatet<goto_programt::const_targett> &loop)
14+
{
15+
goto_programt::const_targett loop_end = loop_head;
16+
for(const auto &t : loop)
17+
{
18+
// an instruction is the loop end if it is a goto instruction
19+
// and it jumps backward to the loop head
20+
if(
21+
t->is_goto() && t->get_target() == loop_head &&
22+
t->location_number > loop_end->location_number)
23+
loop_end = t;
24+
}
25+
INVARIANT(
26+
loop_head != loop_end,
27+
"Could not find end of the loop starting at: " +
28+
loop_head->source_location().as_string());
29+
30+
return loop_end;
31+
}
32+
33+
goto_programt::targett get_loop_end_from_loop_head_and_content_mutable(
34+
const goto_programt::targett &loop_head,
35+
const loop_templatet<goto_programt::targett> &loop)
36+
{
37+
goto_programt::targett loop_end = loop_head;
38+
for(const auto &t : loop)
39+
{
40+
// an instruction is the loop end if it is a goto instruction
41+
// and it jumps backward to the loop head
42+
if(
43+
t->is_goto() && t->get_target() == loop_head &&
44+
t->location_number > loop_end->location_number)
45+
loop_end = t;
46+
}
47+
INVARIANT(
48+
loop_head != loop_end,
49+
"Could not find end of the loop starting at: " +
50+
loop_head->source_location().as_string());
51+
52+
return loop_end;
53+
}
54+
55+
goto_programt::targett get_loop_head_or_end(
56+
const unsigned int target_loop_number,
57+
goto_functiont &function,
58+
bool finding_head)
59+
{
60+
natural_loops_mutablet natural_loops(function.body);
61+
62+
// iterate over all natural loops to find the loop with `target_loop_number`
63+
for(const auto &loop_p : natural_loops.loop_map)
64+
{
65+
const goto_programt::targett loop_head = loop_p.first;
66+
goto_programt::targett loop_end =
67+
get_loop_end_from_loop_head_and_content_mutable(loop_head, loop_p.second);
68+
// check if the current loop is the target loop by comparing loop number
69+
if(loop_end->loop_number == target_loop_number)
70+
{
71+
if(finding_head)
72+
return loop_head;
73+
else
74+
return loop_end;
75+
}
76+
}
77+
78+
UNREACHABLE;
79+
}
80+
81+
goto_programt::targett
82+
get_loop_end(const unsigned int target_loop_number, goto_functiont &function)
83+
{
84+
return get_loop_head_or_end(target_loop_number, function, false);
85+
}
86+
87+
goto_programt::targett
88+
get_loop_head(const unsigned int target_loop_number, goto_functiont &function)
89+
{
90+
return get_loop_head_or_end(target_loop_number, function, true);
91+
}
92+
93+
void annotate_invariants(
94+
const invariant_mapt &invariant_map,
95+
goto_modelt &goto_model,
96+
messaget &log)
97+
{
98+
for(const auto &invariant_map_entry : invariant_map)
99+
{
100+
loop_idt loop_id = invariant_map_entry.first;
101+
irep_idt function_id = loop_id.function_id;
102+
unsigned int loop_number = loop_id.loop_number;
103+
104+
// get the last instruction of the target loop
105+
auto &function = goto_model.goto_functions.function_map[function_id];
106+
goto_programt::targett loop_end = get_loop_end(loop_number, function);
107+
108+
// annotate the invariant to the condition of `loop_end`
109+
exprt condition = loop_end->condition();
110+
loop_end->condition_nonconst().add(ID_C_spec_loop_invariant) =
111+
invariant_map_entry.second;
112+
}
113+
}

0 commit comments

Comments
 (0)