Skip to content

Commit 1424e6e

Browse files
committed
BMC with iterative constraint strengthening
This implements "iterative constraint strenthening" in the word-level BMC engine (VMCAI 2009, Query-driven Program Testing), and makes it the default. The previous, assumption-based method remains available with --bmc-with-assumptions. When using MiniSat, the benefit on hwmcc08 is barely measurable. When using Cadical, the benefit is around 10%. MiniSat iterative 88.27s MiniSat asumptions 88.73s Cadical iterative 103.41s Cadical asumptions 117.74s
1 parent de4ca8c commit 1424e6e

File tree

9 files changed

+209
-69
lines changed

9 files changed

+209
-69
lines changed

regression/ebmc/example4_trace/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ example4.sv
33
--bound 10 --trace
44
^\[counter\.assert\.1\] .* PROVED up to bound 10$
55
^\[counter\.assert\.2\] .* REFUTED$
6-
^ counter\.state = 254 \(11111110\)$
6+
^ counter\.state = \d+ \([01]+\)$
77
^EXIT=10$
88
^SIGNAL=0$

regression/verilog/functioncall/functioncall2.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ functioncall2.v
33
--module main --bound 1
44
^EXIT=0$
55
^SIGNAL=0$
6-
^UNSAT: No counterexample found within bound$
6+
^UNSAT: .*$
77
--
88
^warning: ignoring

regression/verilog/functioncall/functioncall3.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ functioncall3.v
33
--module main --bound 0
44
^EXIT=0$
55
^SIGNAL=0$
6-
^UNSAT: No counterexample found within bound$
6+
^UNSAT: .*$
77
--
88
^warning: ignoring

regression/verilog/functioncall/functioncall4.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ functioncall4.v
33
--module main --bound 0
44
^EXIT=0$
55
^SIGNAL=0$
6-
^UNSAT: No counterexample found within bound$
6+
^UNSAT: .*$
77
--
88
^warning: ignoring
99
--

src/ebmc/bmc.cpp

Lines changed: 198 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,184 @@ Author: Daniel Kroening, [email protected]
1717
#include <chrono>
1818
#include <fstream>
1919

20+
void bmc_with_assumptions(
21+
std::size_t bound,
22+
const transition_systemt &transition_system,
23+
ebmc_propertiest &properties,
24+
decision_proceduret &solver,
25+
message_handlert &message_handler)
26+
{
27+
messaget message(message_handler);
28+
const namespacet ns(transition_system.symbol_table);
29+
30+
// Use assumptions to check the properties separately
31+
32+
for(auto &property : properties.properties)
33+
{
34+
if(property.is_disabled() || property.is_failure() || property.is_assumed())
35+
{
36+
continue;
37+
}
38+
39+
message.status() << "Checking " << property.name << messaget::eom;
40+
41+
auto assumption = not_exprt{conjunction(property.timeframe_handles)};
42+
43+
decision_proceduret::resultt dec_result = solver(assumption);
44+
45+
switch(dec_result)
46+
{
47+
case decision_proceduret::resultt::D_SATISFIABLE:
48+
if(property.is_exists_path())
49+
{
50+
property.proved();
51+
message.result() << "SAT: path found" << messaget::eom;
52+
}
53+
else // universal path property
54+
{
55+
property.refuted();
56+
message.result() << "SAT: counterexample found" << messaget::eom;
57+
}
58+
59+
property.witness_trace = compute_trans_trace(
60+
property.timeframe_handles,
61+
solver,
62+
bound + 1,
63+
ns,
64+
transition_system.main_symbol->name);
65+
break;
66+
67+
case decision_proceduret::resultt::D_UNSATISFIABLE:
68+
if(property.is_exists_path())
69+
{
70+
message.result() << "UNSAT: No path found within bound"
71+
<< messaget::eom;
72+
property.refuted_with_bound(bound);
73+
}
74+
else // universal path property
75+
{
76+
message.result() << "UNSAT: No counterexample found within bound"
77+
<< messaget::eom;
78+
property.proved_with_bound(bound);
79+
}
80+
break;
81+
82+
case decision_proceduret::resultt::D_ERROR:
83+
message.error() << "Error from decision procedure" << messaget::eom;
84+
property.failure();
85+
break;
86+
87+
default:
88+
property.failure();
89+
throw ebmc_errort() << "Unexpected result from decision procedure";
90+
}
91+
}
92+
}
93+
94+
/// VMCAI 2009 Query-driven program testing,
95+
/// "iterative constraint strengthening"
96+
void bmc_with_iterative_constraint_strengthening(
97+
std::size_t bound,
98+
const transition_systemt &transition_system,
99+
ebmc_propertiest &properties,
100+
decision_proceduret &solver,
101+
message_handlert &message_handler)
102+
{
103+
messaget message(message_handler);
104+
const namespacet ns(transition_system.symbol_table);
105+
106+
auto trace_found = [&solver](const ebmc_propertiest::propertyt &property)
107+
{
108+
for(auto &h : property.timeframe_handles)
109+
if(solver.get(h).is_false())
110+
return true;
111+
return false;
112+
};
113+
114+
while(true)
115+
{
116+
// At least one of the remaining properties
117+
// should be falsified.
118+
exprt::operandst disjuncts;
119+
120+
for(auto &property : properties.properties)
121+
{
122+
if(property.is_unknown())
123+
{
124+
for(auto &h : property.timeframe_handles)
125+
disjuncts.push_back(not_exprt{h});
126+
}
127+
}
128+
129+
// This constraint is strenthened in each iteration.
130+
solver.set_to_true(disjunction(disjuncts));
131+
132+
decision_proceduret::resultt dec_result = solver();
133+
134+
switch(dec_result)
135+
{
136+
case decision_proceduret::resultt::D_SATISFIABLE:
137+
// Found a trace for at least one further property with unknown state
138+
message.result() << "SAT: path found" << messaget::eom;
139+
140+
for(auto &property : properties.properties)
141+
{
142+
if(property.is_unknown() && trace_found(property))
143+
{
144+
if(property.is_exists_path())
145+
property.proved();
146+
else // universal path property
147+
property.refuted();
148+
149+
property.witness_trace = compute_trans_trace(
150+
property.timeframe_handles,
151+
solver,
152+
bound + 1,
153+
ns,
154+
transition_system.main_symbol->name);
155+
}
156+
}
157+
break; // next iteration of while loop
158+
159+
case decision_proceduret::resultt::D_UNSATISFIABLE:
160+
message.result() << "UNSAT: No path found within bound" << messaget::eom;
161+
162+
for(auto &property : properties.properties)
163+
{
164+
if(property.is_unknown())
165+
{
166+
if(property.is_exists_path())
167+
property.refuted_with_bound(bound);
168+
else // universal path property
169+
property.proved_with_bound(bound);
170+
}
171+
}
172+
return;
173+
174+
case decision_proceduret::resultt::D_ERROR:
175+
for(auto &property : properties.properties)
176+
{
177+
if(property.is_unknown())
178+
property.failure();
179+
}
180+
message.error() << "Error from decision procedure" << messaget::eom;
181+
return;
182+
183+
default:
184+
for(auto &property : properties.properties)
185+
{
186+
if(property.is_unknown())
187+
property.failure();
188+
}
189+
throw ebmc_errort() << "Unexpected result from decision procedure";
190+
}
191+
}
192+
}
193+
20194
void bmc(
21195
std::size_t bound,
22196
bool convert_only,
197+
bool bmc_with_assumptions,
23198
const transition_systemt &transition_system,
24199
ebmc_propertiest &properties,
25200
const ebmc_solver_factoryt &solver_factory,
@@ -76,12 +251,25 @@ void bmc(
76251

77252
if(convert_only)
78253
{
254+
// At least one property must be violated in at least one
255+
// timeframe.
256+
exprt::operandst disjuncts;
257+
79258
for(const auto &property : properties.properties)
80259
{
81-
if(!property.is_disabled())
82-
solver.set_to_false(conjunction(property.timeframe_handles));
260+
if(property.is_assumed())
261+
{
262+
solver.set_to_true(conjunction(property.timeframe_handles));
263+
}
264+
else if(!property.is_disabled())
265+
{
266+
for(auto &h : property.timeframe_handles)
267+
disjuncts.push_back(not_exprt{h});
268+
}
83269
}
84270

271+
solver.set_to_true(disjunction(disjuncts));
272+
85273
// Call decision_proceduret::dec_solve to finish the conversion
86274
// process.
87275
(void)solver();
@@ -93,69 +281,15 @@ void bmc(
93281

94282
auto sat_start_time = std::chrono::steady_clock::now();
95283

96-
// Use assumptions to check the properties separately
97-
98-
for(auto &property : properties.properties)
284+
if(bmc_with_assumptions)
99285
{
100-
if(
101-
property.is_disabled() || property.is_failure() ||
102-
property.is_assumed())
103-
{
104-
continue;
105-
}
106-
107-
message.status() << "Checking " << property.name << messaget::eom;
108-
109-
auto assumption = not_exprt{conjunction(property.timeframe_handles)};
110-
111-
decision_proceduret::resultt dec_result = solver(assumption);
112-
113-
switch(dec_result)
114-
{
115-
case decision_proceduret::resultt::D_SATISFIABLE:
116-
if(property.is_exists_path())
117-
{
118-
property.proved();
119-
message.result() << "SAT: path found" << messaget::eom;
120-
}
121-
else // universal path property
122-
{
123-
property.refuted();
124-
message.result() << "SAT: counterexample found" << messaget::eom;
125-
}
126-
127-
property.witness_trace = compute_trans_trace(
128-
property.timeframe_handles,
129-
solver,
130-
bound + 1,
131-
ns,
132-
transition_system.main_symbol->name);
133-
break;
134-
135-
case decision_proceduret::resultt::D_UNSATISFIABLE:
136-
if(property.is_exists_path())
137-
{
138-
message.result() << "UNSAT: No path found within bound"
139-
<< messaget::eom;
140-
property.refuted_with_bound(bound);
141-
}
142-
else // universal path property
143-
{
144-
message.result() << "UNSAT: No counterexample found within bound"
145-
<< messaget::eom;
146-
property.proved_with_bound(bound);
147-
}
148-
break;
149-
150-
case decision_proceduret::resultt::D_ERROR:
151-
message.error() << "Error from decision procedure" << messaget::eom;
152-
property.failure();
153-
break;
154-
155-
default:
156-
property.failure();
157-
throw ebmc_errort() << "Unexpected result from decision procedure";
158-
}
286+
::bmc_with_assumptions(
287+
bound, transition_system, properties, solver, message_handler);
288+
}
289+
else
290+
{
291+
::bmc_with_iterative_constraint_strengthening(
292+
bound, transition_system, properties, solver, message_handler);
159293
}
160294

161295
auto sat_stop_time = std::chrono::steady_clock::now();

src/ebmc/bmc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class transition_systemt;
2222
void bmc(
2323
std::size_t bound,
2424
bool convert_only,
25+
bool bmc_with_assumptions,
2526
const transition_systemt &,
2627
ebmc_propertiest &,
2728
const ebmc_solver_factoryt &,

src/ebmc/ebmc_parse_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class ebmc_parse_optionst:public parse_options_baset
4747
"(compute-ct)(dot-netlist)(smv-netlist)(vcd):"
4848
"(random-traces)(trace-steps):(random-seed):(traces):"
4949
"(random-trace)(random-waveform)"
50+
"(bmc-with-assumptions)"
5051
"(liveness-to-safety)"
5152
"I:D:(preprocess)(systemverilog)(vl2smv-extensions)"
5253
"(warn-implicit-nets)",

src/ebmc/k_induction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ void k_inductiont::induction_base()
202202

203203
bmc(
204204
k,
205-
false,
205+
false, // convert_only
206+
false, // bmc_with_assumptions
206207
transition_system,
207208
properties,
208209
solver_factory,

src/ebmc/property_checker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,12 @@ property_checker_resultt word_level_bmc(
8080
if(properties.properties.empty())
8181
throw "no properties";
8282

83+
bool bmc_with_assumptions = cmdline.isset("bmc-with-assumptions");
84+
8385
bmc(
8486
bound,
8587
convert_only,
88+
bmc_with_assumptions,
8689
transition_system,
8790
properties,
8891
solver_factory,

0 commit comments

Comments
 (0)