Skip to content

Commit 333c256

Browse files
authored
Merge pull request #7268 from diffblue/chc_generalization
CHC solver: generalization
2 parents 7aaff0d + 38aabd6 commit 333c256

File tree

13 files changed

+331
-46
lines changed

13 files changed

+331
-46
lines changed

regression/cprover/loops/assigns1.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
int main()
22
{
3-
int *p;
4-
__CPROVER_assume(__CPROVER_r_ok(p));
3+
int x = 123;
54

65
for(int i = 0; i < 10; i++)
76
{
8-
// does not assign p
7+
// does not assign x
98
}
109

11-
__CPROVER_assert(__CPROVER_r_ok(p), "property 1"); // passes
10+
__CPROVER_assert(x == 123, "property 1"); // passes
1211
}

regression/cprover/loops/assigns1.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
assigns1.c
33

44
^EXIT=0$

regression/cprover/loops/assigns2.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main()
2+
{
3+
int x = 123;
4+
5+
// The loops do not assign x.
6+
for(int i = 0; i < 10; i++)
7+
;
8+
for(int i = 0; i < 10; i++)
9+
;
10+
11+
__CPROVER_assert(x == 123, "property 1"); // passes
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
assigns2.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$
7+
--
8+
--

src/cprover/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SRC = address_taken.cpp \
1212
flatten_ok_expr.cpp \
1313
format_hooks.cpp \
1414
free_symbols.cpp \
15+
generalization.cpp \
1516
help_formatter.cpp \
1617
inductiveness.cpp \
1718
instrument_contracts.cpp \

src/cprover/generalization.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*******************************************************************\
2+
3+
Module: Generalization
4+
5+
Author: Daniel Kroening, dkr@amazon.com
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Generalization
11+
12+
#include "generalization.h"
13+
14+
#include <util/format_expr.h>
15+
16+
#include "console.h"
17+
#include "solver.h"
18+
19+
#include <iostream>
20+
#include <map>
21+
22+
class frequency_mapt
23+
{
24+
public:
25+
explicit frequency_mapt(const exprt &expr)
26+
{
27+
setup_rec(expr);
28+
}
29+
30+
void operator()(const exprt &expr)
31+
{
32+
count_rec(expr);
33+
}
34+
35+
using counterst = std::map<exprt, std::size_t>;
36+
37+
// return frequencies, highest to lowest
38+
std::vector<counterst::const_iterator> frequencies() const
39+
{
40+
std::vector<counterst::const_iterator> result;
41+
for(auto it = counters.begin(); it != counters.end(); it++)
42+
result.push_back(it);
43+
std::sort(
44+
result.begin(),
45+
result.end(),
46+
[](counterst::const_iterator a, counterst::const_iterator b) -> bool {
47+
return a->second >= b->second;
48+
});
49+
return result;
50+
}
51+
52+
protected:
53+
counterst counters;
54+
55+
void count_rec(const exprt &expr)
56+
{
57+
if(expr.id() == ID_or)
58+
{
59+
for(auto &op : expr.operands())
60+
count_rec(op);
61+
}
62+
else
63+
{
64+
auto it = counters.find(expr);
65+
if(it != counters.end())
66+
it->second++;
67+
}
68+
}
69+
70+
void setup_rec(const exprt &expr)
71+
{
72+
if(expr.id() == ID_or)
73+
{
74+
for(auto &op : expr.operands())
75+
setup_rec(op);
76+
}
77+
else
78+
counters.emplace(expr, 0);
79+
}
80+
};
81+
82+
void generalization(
83+
std::vector<framet> &frames,
84+
const workt &dropped,
85+
const propertyt &property,
86+
const solver_optionst &solver_options)
87+
{
88+
// Look at the frame where we've given up
89+
auto &frame = frames[dropped.frame.index];
90+
91+
if(solver_options.verbose)
92+
{
93+
for(auto &invariant : frame.invariants)
94+
{
95+
std::cout << consolet::green << "GI " << format(invariant)
96+
<< consolet::reset << '\n';
97+
}
98+
}
99+
100+
// We generalize by dropping disjuncts.
101+
// Look at the frequencies of the disjuncts in the proof obligation
102+
// among the candidate invariant and the previous obligations.
103+
frequency_mapt frequency_map(dropped.invariant);
104+
105+
for(auto &i : frame.invariants)
106+
frequency_map(i);
107+
108+
for(auto &o : frame.obligations)
109+
frequency_map(o);
110+
111+
const auto frequencies = frequency_map.frequencies();
112+
113+
if(solver_options.verbose)
114+
{
115+
for(auto entry : frequencies)
116+
{
117+
std::cout << "Freq " << entry->second << " " << format(entry->first)
118+
<< "\n";
119+
}
120+
}
121+
122+
// form disjunction of top ones
123+
exprt::operandst disjuncts;
124+
for(auto entry : frequencies)
125+
{
126+
if(entry->second == frequencies.front()->second)
127+
disjuncts.push_back(entry->first);
128+
}
129+
130+
// Does this actually strengthen the formula?
131+
if(disjuncts.size() < frequencies.size())
132+
{
133+
auto new_invariant = disjunction(disjuncts);
134+
frame.add_invariant(new_invariant);
135+
if(solver_options.verbose)
136+
std::cout << consolet::yellow << "NI " << format(new_invariant)
137+
<< consolet::reset << '\n';
138+
}
139+
else
140+
{
141+
// no, give up
142+
}
143+
}

src/cprover/generalization.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*******************************************************************\
2+
3+
Module: Generalization
4+
5+
Author: Daniel Kroening, dkr@amazon.com
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Generalization
11+
12+
#ifndef CPROVER_CPROVER_GENERALIZATION_H
13+
#define CPROVER_CPROVER_GENERALIZATION_H
14+
15+
#include "solver_types.h"
16+
17+
class solver_optionst;
18+
19+
void generalization(
20+
std::vector<framet> &frames,
21+
const workt &dropped,
22+
const propertyt &,
23+
const solver_optionst &);
24+
25+
#endif // CPROVER_CPROVER_GENERALIZATION_H

src/cprover/inductiveness.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Author: Daniel Kroening, dkr@amazon.com
3131
bool is_subsumed(
3232
const std::unordered_set<exprt, irep_hash> &a1,
3333
const std::unordered_set<exprt, irep_hash> &a2,
34+
const std::unordered_set<exprt, irep_hash> &a3,
3435
const exprt &b,
3536
const std::unordered_set<symbol_exprt, irep_hash> &address_taken,
3637
bool verbose,
@@ -64,6 +65,9 @@ bool is_subsumed(
6465
for(auto &a_conjunct : a2)
6566
axioms << a_conjunct;
6667

68+
for(auto &a_conjunct : a3)
69+
axioms << a_conjunct;
70+
6771
axioms.set_to_false(b);
6872

6973
// instantiate our axioms
@@ -136,14 +140,21 @@ inductiveness_resultt inductiveness_check(
136140

137141
if(solver_options.verbose)
138142
{
139-
// print the current invariants in the frame
143+
// print the current invariants and obligations in the frame
140144
for(const auto &invariant : f.invariants)
141145
{
142146
std::cout << consolet::faint << consolet::blue;
143147
std::cout << 'I' << std::setw(2) << frame_ref.index << ' ';
144148
std::cout << format(invariant);
145149
std::cout << consolet::reset << '\n';
146150
}
151+
for(const auto &obligation : f.obligations)
152+
{
153+
std::cout << consolet::faint << consolet::blue;
154+
std::cout << 'O' << std::setw(2) << frame_ref.index << ' ';
155+
std::cout << format(obligation);
156+
std::cout << consolet::reset << '\n';
157+
}
147158
}
148159

149160
if(solver_options.verbose)
@@ -158,6 +169,7 @@ inductiveness_resultt inductiveness_check(
158169
}
159170
else if(is_subsumed(
160171
f.invariants_set,
172+
f.obligations_set,
161173
f.auxiliaries_set,
162174
invariant,
163175
address_taken,
@@ -182,7 +194,7 @@ inductiveness_resultt inductiveness_check(
182194
std::cout << format(invariant) << '\n';
183195

184196
// store in frame
185-
frames[frame_ref.index].add_invariant(invariant);
197+
frames[frame_ref.index].add_obligation(invariant);
186198

187199
// add to queue
188200
auto new_path = path;
@@ -191,14 +203,21 @@ inductiveness_resultt inductiveness_check(
191203
}
192204
};
193205

194-
// stick non-true frames into the queue
206+
// stick invariants into the queue
195207
for(std::size_t frame_index = 0; frame_index < frames.size(); frame_index++)
196208
{
197209
frame_reft frame_ref(frame_index);
198210
for(auto &cond : frames[frame_index].invariants)
199211
queue.emplace_back(frame_ref, cond, workt::patht{frame_ref});
200212
}
201213

214+
// clean up the obligations
215+
for(auto &frame : frames)
216+
{
217+
frame.obligations.clear();
218+
frame.obligations_set.clear();
219+
}
220+
202221
while(!queue.empty())
203222
{
204223
auto work = queue.back();
@@ -218,16 +237,17 @@ inductiveness_resultt inductiveness_check(
218237
if(counterexample_found)
219238
{
220239
property.trace = counterexample_found.value();
221-
return inductiveness_resultt::BASE_CASE_FAIL;
240+
return inductiveness_resultt::base_case_fail(std::move(work));
222241
}
223242

224243
propagate(
225244
frames, work, address_taken, solver_options.verbose, ns, propagator);
245+
246+
// did we drop anything?
247+
if(!dropped.empty())
248+
return inductiveness_resultt::step_case_fail(std::move(dropped.front()));
226249
}
227250

228-
// did we drop anything?
229-
if(dropped.empty())
230-
return inductiveness_resultt::INDUCTIVE;
231-
else
232-
return inductiveness_resultt::STEP_CASE_FAIL;
251+
// done, saturated
252+
return inductiveness_resultt::inductive();
233253
}

src/cprover/inductiveness.h

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,41 @@ Author: Daniel Kroening, dkr@amazon.com
1616

1717
class solver_optionst;
1818

19-
enum inductiveness_resultt
19+
class inductiveness_resultt
2020
{
21-
INDUCTIVE,
22-
BASE_CASE_FAIL,
23-
STEP_CASE_FAIL
21+
public:
22+
enum outcomet
23+
{
24+
INDUCTIVE,
25+
BASE_CASE_FAIL,
26+
STEP_CASE_FAIL
27+
} outcome;
28+
29+
static inductiveness_resultt inductive()
30+
{
31+
return inductiveness_resultt(INDUCTIVE);
32+
}
33+
34+
static inductiveness_resultt base_case_fail(workt refuted)
35+
{
36+
auto result = inductiveness_resultt(BASE_CASE_FAIL);
37+
result.work = std::move(refuted);
38+
return result;
39+
}
40+
41+
static inductiveness_resultt step_case_fail(workt dropped)
42+
{
43+
auto result = inductiveness_resultt(STEP_CASE_FAIL);
44+
result.work = std::move(dropped);
45+
return result;
46+
}
47+
48+
optionalt<workt> work;
49+
50+
private:
51+
explicit inductiveness_resultt(outcomet __outcome) : outcome(__outcome)
52+
{
53+
}
2454
};
2555

2656
inductiveness_resultt inductiveness_check(

src/cprover/propagate.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ void propagate(
7575
auto &state = to_symbol_expr(function_application.function());
7676
auto cond1 = to_and_expr(implication.lhs).op1();
7777
auto cond2 = implies_exprt(cond1, simplified2);
78-
propagator(state, cond2, work.path);
78+
auto simplified = simplify_expr(cond2, ns);
79+
propagator(state, simplified, work.path);
7980
}
8081
}
8182
}

0 commit comments

Comments
 (0)