Skip to content

Commit b91b168

Browse files
committed
Add enumerative loop invariant synthesizer
1 parent 3e490c9 commit b91b168

File tree

26 files changed

+1385
-31
lines changed

26 files changed

+1385
-31
lines changed

regression/contracts/loop_invariant_synthesis_01/test.desc renamed to regression/contracts/loop_contract_synthesis_01/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CORE
22
main.c
3-
--synthesize-loop-invariants --apply-loop-contracts
3+
--synthesize-loop-invariants
44
^EXIT=0$
55
^SIGNAL=0$
66
^\[main\.\d+\] line 10 Check loop invariant before entry: SUCCESS$
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#define SIZE 80
2+
3+
void main()
4+
{
5+
unsigned len;
6+
__CPROVER_assume(len <= SIZE);
7+
__CPROVER_assume(len >= 8);
8+
char *array = malloc(len);
9+
unsigned s = 0;
10+
11+
for(unsigned i = 0; i < SIZE; ++i)
12+
__CPROVER_assigns(i, s)
13+
{
14+
if(i == len-1)
15+
break;
16+
s += array[i];
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--pointer-check --synthesize-loop-invariants
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.pointer\_dereference.\d+\] .* SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
10+
This test shows that loop invariant with form of range predicates can be correctly
11+
synthesized for programs with only pointer checks but no other assertions.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#define SIZE 80
2+
3+
void main()
4+
{
5+
unsigned long len;
6+
__CPROVER_assume(len <= SIZE);
7+
__CPROVER_assume(len >= 8);
8+
char *array = malloc(len);
9+
const char *end = array + len;
10+
unsigned s = 0;
11+
12+
while(array != end)
13+
{
14+
s += *array++;
15+
}
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--pointer-check --synthesize-loop-invariants
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.pointer\_dereference.\d+\] .* SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
10+
This test shows that loop invariant with form of range predicates and same
11+
object predicates can be correctly synthesized for programs with only pointer
12+
checks but no other assertions.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#define SIZE 80
2+
3+
void main()
4+
{
5+
unsigned long len;
6+
__CPROVER_assume(len <= SIZE);
7+
__CPROVER_assume(len >= 8);
8+
char *array = malloc(len);
9+
unsigned long s = 0;
10+
11+
unsigned long j = 0;
12+
for(unsigned long i = 0; i < len; i++)
13+
{
14+
s += array[j];
15+
j++;
16+
}
17+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--pointer-check --synthesize-loop-invariants
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.pointer\_dereference.\d+\] .* SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
10+
This test shows that the loop-invariant synthesizer can enumerate
11+
strengthening clauses for invariant-not-preserved violation.

src/analyses/dependence_graph.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,53 @@ void dependence_grapht::add_dep(
383383
nodes[n_to].in[n_from].add(kind);
384384
}
385385

386+
bool dependence_grapht::is_flow_dependent(
387+
const goto_programt::const_targett &from,
388+
const goto_programt::const_targett &to)
389+
{
390+
std::set<node_indext> visited = std::set<node_indext>();
391+
const dep_graph_domaint from_domain = static_cast<const dep_graph_domaint &>(
392+
*storage->abstract_state_before(from, *domain_factory));
393+
const dep_graph_domaint to_domain = static_cast<const dep_graph_domaint &>(
394+
*storage->abstract_state_before(to, *domain_factory));
395+
return is_flow_dependent(from_domain, to_domain, visited);
396+
}
397+
398+
bool dependence_grapht::is_flow_dependent(
399+
const dep_graph_domaint &from,
400+
const dep_graph_domaint &to,
401+
std::set<node_indext> &visited)
402+
{
403+
// `to` is control dependent on `from`?
404+
for(const auto node : to.get_control_deps())
405+
{
406+
if(visited.count((*this)[node].get_node_id()))
407+
continue;
408+
409+
visited.insert((*this)[node].get_node_id());
410+
411+
if(
412+
from.get_node_id() == (*this)[node].get_node_id() ||
413+
is_flow_dependent(from, (*this)[node], visited))
414+
return true;
415+
}
416+
417+
// `to` is data dependent on `from`?
418+
for(const auto node : to.get_data_deps())
419+
{
420+
if(visited.count((*this)[node].get_node_id()))
421+
continue;
422+
423+
visited.insert((*this)[node].get_node_id());
424+
425+
if(
426+
from.get_node_id() == (*this)[node].get_node_id() ||
427+
is_flow_dependent(from, (*this)[node], visited))
428+
return true;
429+
}
430+
return false;
431+
}
432+
386433
void dep_graph_domaint::populate_dep_graph(
387434
dependence_grapht &dep_graph, goto_programt::const_targett this_loc) const
388435
{

src/analyses/dependence_graph.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class dep_graph_domaint:public ai_domain_baset
6767
{
6868
public:
6969
typedef grapht<dep_nodet>::node_indext node_indext;
70+
typedef std::set<goto_programt::const_targett> depst;
7071

7172
explicit dep_graph_domaint(node_indext id)
7273
: has_values(false), node_id(id), has_changed(false)
@@ -167,13 +168,20 @@ class dep_graph_domaint:public ai_domain_baset
167168
void populate_dep_graph(
168169
dependence_grapht &, goto_programt::const_targett) const;
169170

171+
depst get_control_deps() const
172+
{
173+
return control_deps;
174+
}
175+
depst get_data_deps() const
176+
{
177+
return data_deps;
178+
}
179+
170180
private:
171181
tvt has_values;
172182
node_indext node_id;
173183
bool has_changed;
174184

175-
typedef std::set<goto_programt::const_targett> depst;
176-
177185
// Set of locations with control instructions on which the instruction at this
178186
// location has a control dependency on
179187
depst control_deps;
@@ -279,7 +287,16 @@ class dependence_grapht:
279287
return rd;
280288
}
281289

290+
/// Decide whether the instruction `to` is flow dependent on `from`.
291+
bool is_flow_dependent(
292+
const goto_programt::const_targett &from,
293+
const goto_programt::const_targett &to);
294+
282295
protected:
296+
bool is_flow_dependent(
297+
const dep_graph_domaint &from,
298+
const dep_graph_domaint &to,
299+
std::set<node_indext> &visited);
283300
friend dep_graph_domain_factoryt;
284301
friend dep_graph_domaint;
285302
const namespacet &ns;

0 commit comments

Comments
 (0)