Skip to content

Commit 2ee11ea

Browse files
committed
BMC: add support for LTL X operator
This adds support for the LTL X operator to the BMC engine.
1 parent f77b914 commit 2ee11ea

File tree

5 files changed

+101
-41
lines changed

5 files changed

+101
-41
lines changed

regression/ebmc/smv/smv_ltlspec3.desc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
smv_ltlspec3.smv
3+
--bound 10 --numbered-trace
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main::spec1\] X X main::var::x = 1: REFUTED$
7+
^main::var::x@0 = 0$
8+
^main::var::x@1 = 0$
9+
^main::var::x@2 = 0$
10+
--
11+
^warning: ignoring

regression/ebmc/smv/smv_ltlspec3.smv

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
MODULE main
2+
3+
VAR x : 0..3;
4+
5+
ASSIGN
6+
init(x) := 0;
7+
next(x) := x;
8+
9+
-- we want to see a trace with three states
10+
LTLSPEC X X x=1

src/temporal-logic/temporal_expr.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,10 @@ static inline A_exprt &to_A_expr(exprt &expr)
235235
return static_cast<A_exprt &>(expr);
236236
}
237237

238-
class X_exprt : public binary_predicate_exprt
238+
class X_exprt : public unary_predicate_exprt
239239
{
240240
public:
241-
explicit X_exprt(exprt op0, exprt op1)
242-
: binary_predicate_exprt(std::move(op0), ID_X, std::move(op1))
241+
explicit X_exprt(exprt op) : unary_predicate_exprt(ID_X, std::move(op))
243242
{
244243
}
245244
};

src/trans-word-level/instantiate_word_level.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Author: Daniel Kroening, [email protected]
1010

1111
#include <util/ebmc_util.h>
1212

13+
#include <temporal-logic/temporal_expr.h>
1314
#include <verilog/sva_expr.h>
1415

1516
#include "property.h"
@@ -235,6 +236,17 @@ exprt wl_instantiatet::instantiate_rec(exprt expr, const mp_integer &t) const
235236
else
236237
return true_exprt(); // works on NNF only
237238
}
239+
else if(expr.id() == ID_X)
240+
{
241+
const auto next = t + 1;
242+
243+
if(next < no_timeframes)
244+
{
245+
return instantiate_rec(to_X_expr(expr).op(), next);
246+
}
247+
else
248+
return true_exprt(); // works on NNF only
249+
}
238250
else if(expr.id() == ID_sva_eventually)
239251
{
240252
const auto &eventually_expr = to_sva_eventually_expr(expr);

src/trans-word-level/property.cpp

+66-38
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ bool bmc_supports_property(const exprt &expr)
5252
return true;
5353
else if(expr.id() == ID_G)
5454
return true;
55+
else if(expr.id() == ID_X)
56+
return bmc_supports_property(to_X_expr(expr).op());
5557
else if(expr.id() == ID_sva_always)
5658
return true;
5759
else
@@ -60,7 +62,7 @@ bool bmc_supports_property(const exprt &expr)
6062

6163
/*******************************************************************\
6264
63-
Function: property
65+
Function: property_obligations_rec
6466
6567
Inputs:
6668
@@ -70,51 +72,77 @@ Function: property
7072
7173
\*******************************************************************/
7274

73-
std::map<std::size_t, exprt::operandst> property_obligations(
75+
void property_obligations_rec(
7476
const exprt &property_expr,
75-
message_handlert &message_handler,
7677
decision_proceduret &solver,
78+
std::size_t current,
7779
std::size_t no_timeframes,
78-
const namespacet &ns)
80+
const namespacet &ns,
81+
std::map<std::size_t, exprt::operandst> &obligations)
7982
{
80-
PRECONDITION(no_timeframes > 0);
83+
PRECONDITION(current < no_timeframes);
8184

82-
std::map<std::size_t, exprt::operandst> obligations;
83-
84-
messaget message(message_handler);
85-
86-
// Initial state only property?
87-
if(
88-
!is_temporal_operator(property_expr) ||
89-
property_expr.id() == ID_sva_cycle_delay ||
90-
property_expr.id() == ID_sva_nexttime ||
91-
property_expr.id() == ID_sva_s_nexttime)
85+
if(property_expr.id() == ID_X)
9286
{
93-
exprt tmp = instantiate(property_expr, 0, no_timeframes, ns);
94-
obligations[0].push_back(solver.handle(tmp));
95-
return obligations;
87+
auto next = current + 1;
88+
if(next < no_timeframes)
89+
{
90+
auto &op = to_X_expr(property_expr).op();
91+
property_obligations_rec(
92+
op, solver, next, no_timeframes, ns, obligations);
93+
}
9694
}
95+
else if(
96+
property_expr.id() == ID_AG || property_expr.id() == ID_G ||
97+
property_expr.id() == ID_sva_always)
98+
{
99+
// We want AG phi.
100+
auto &phi = [](const exprt &expr) -> const exprt & {
101+
if(expr.id() == ID_AG)
102+
return to_AG_expr(expr).op();
103+
else if(expr.id() == ID_G)
104+
return to_G_expr(expr).op();
105+
else if(expr.id() == ID_sva_always)
106+
return to_sva_always_expr(expr).op();
107+
else
108+
PRECONDITION(false);
109+
}(property_expr);
110+
111+
for(std::size_t c = current; c < no_timeframes; c++)
112+
{
113+
property_obligations_rec(phi, solver, c, no_timeframes, ns, obligations);
114+
}
115+
}
116+
else
117+
{
118+
// current state property
119+
exprt tmp = instantiate(property_expr, current, no_timeframes, ns);
120+
obligations[current].push_back(solver.handle(tmp));
121+
}
122+
}
97123

98-
// We want AG p.
99-
auto &p = [](const exprt &expr) -> const exprt & {
100-
if(expr.id() == ID_AG)
101-
return to_AG_expr(expr).op();
102-
else if(expr.id() == ID_G)
103-
return to_G_expr(expr).op();
104-
else if(expr.id() == ID_sva_always)
105-
return to_sva_always_expr(expr).op();
106-
else
107-
PRECONDITION(false);
108-
}(property_expr);
124+
/*******************************************************************\
109125
110-
for(std::size_t c = 0; c < no_timeframes; c++)
111-
{
112-
exprt tmp=
113-
instantiate(p, c, no_timeframes, ns);
126+
Function: property_obligations
114127
115-
auto handle = solver.handle(tmp);
116-
obligations[c].push_back(std::move(handle));
117-
}
128+
Inputs:
129+
130+
Outputs:
131+
132+
Purpose:
133+
134+
\*******************************************************************/
135+
136+
std::map<std::size_t, exprt::operandst> property_obligations(
137+
const exprt &property_expr,
138+
decision_proceduret &solver,
139+
std::size_t no_timeframes,
140+
const namespacet &ns)
141+
{
142+
std::map<std::size_t, exprt::operandst> obligations;
143+
144+
property_obligations_rec(
145+
property_expr, solver, 0, no_timeframes, ns, obligations);
118146

119147
return obligations;
120148
}
@@ -142,8 +170,8 @@ void property(
142170
// The first element of the pair is the length of the
143171
// counterexample, and the second is the condition that
144172
// must be valid for the property to hold.
145-
auto obligations = property_obligations(
146-
property_expr, message_handler, solver, no_timeframes, ns);
173+
auto obligations =
174+
property_obligations(property_expr, solver, no_timeframes, ns);
147175

148176
// Map obligations onto timeframes.
149177
prop_handles.resize(no_timeframes, true_exprt());

0 commit comments

Comments
 (0)