Skip to content

Commit f84bf71

Browse files
committed
Expand expr2statement_list
Adds support for the conversion of boolean expressions. Note that this will hardly have any effect on the output of CBMC and in the current state only be used by projects which rely on it. There are several STL-specific simplification routines integrated in the conversion, e.g. changing the conversion order of operands to avoid unnecessary nesting.
1 parent ae6dd2f commit f84bf71

File tree

2 files changed

+402
-6
lines changed

2 files changed

+402
-6
lines changed

src/statement-list/converters/expr2statement_list.cpp

Lines changed: 281 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,105 @@ Author: Matthias Weiss, [email protected]
1010

1111
#include <ansi-c/expr2c.h>
1212

13+
#include <util/suffix.h>
14+
#include <util/symbol_table.h>
15+
16+
#include <algorithm>
17+
#include <cstring>
18+
#include <iostream>
19+
20+
/// STL code for an AND instruction.
21+
#define AND 'A'
22+
23+
/// STL code for an OR instruction.
24+
#define OR 'O'
25+
26+
/// STL code for a XOR instruction.
27+
#define XOR 'X'
28+
29+
/// Postfix for any negated boolean instruction.
30+
#define NOT_POSTFIX 'N'
31+
32+
/// STL code for a NOT instruction.
33+
#define NOT "NOT"
34+
35+
/// Separator between the instruction code and it's operand.
36+
#define OPERAND_SEPARATOR ' '
37+
38+
/// Separator between the end of an instruction and the next one.
39+
#define LINE_SEPARATOR ";\n"
40+
41+
/// Separator for the end of an instruction that introduces a new layer of
42+
/// nesting.
43+
#define NESTING_OPEN_LINE_SEPARATOR "(;\n"
44+
45+
/// Separator for the end of an instruction that closes a nesting layer. Also
46+
/// known as the NESTING CLOSED instruction.
47+
#define NESTING_CLOSED_LINE_SEPARATOR ");\n"
48+
49+
/// Prefix for the reference to any variable.
50+
#define REFERENCE_FLAG '#'
51+
52+
/// CBMC-internal separator for variable scopes.
53+
#define SCOPE_SEPARATOR "::"
54+
55+
/// Modifies the parameters of a binary equal expression with the help of its
56+
/// symmetry properties. This function basically converts the operands to
57+
/// operands of a XOR expression so that the whole expression can be treated as
58+
/// such. This can reduce complexity in some cases.
59+
/// \param lhs: Left side of the binary expression.
60+
/// \param rhs: Right side of the binary expression.
61+
/// \return: List of instrumented operands.
62+
static std::vector<exprt>
63+
instrument_equal_operands(const exprt &lhs, const exprt &rhs)
64+
{
65+
std::vector<exprt> result;
66+
if(ID_not != lhs.id() && ID_not != rhs.id())
67+
{
68+
// lhs == rhs is equivalent to X lhs; XN rhs;
69+
result.push_back(lhs);
70+
result.push_back(not_exprt{rhs});
71+
}
72+
else if(ID_not != lhs.id() && ID_not == rhs.id())
73+
{
74+
// lhs == !rhs is equivalent to X lhs; X rhs;
75+
result.push_back(lhs);
76+
result.push_back(rhs.op0());
77+
}
78+
else if(ID_not == lhs.id() && ID_not != rhs.id())
79+
{
80+
// !lhs == rhs is equivalent to X lhs; X rhs;
81+
result.push_back(lhs.op0());
82+
result.push_back(rhs);
83+
}
84+
else // ID_not == lhs.id() && ID_not == rhs.id()
85+
{
86+
// !lhs == !rhs is equivalent to X lhs; XN rhs;
87+
result.push_back(lhs.op0());
88+
result.push_back(rhs);
89+
}
90+
return result;
91+
}
92+
93+
/// Checks if the expression or one of its parameters is not of type bool.
94+
/// \param expr: Expression to verify.
95+
/// \return: True if the expression and its operands are not or only partially
96+
/// of type bool, false otherwise.
97+
static bool is_not_bool(const exprt &expr)
98+
{
99+
if(!expr.is_boolean())
100+
return true;
101+
for(const exprt &op : expr.operands())
102+
if(!op.is_boolean())
103+
return true;
104+
return false;
105+
}
106+
13107
std::string expr2stl(const exprt &expr, const namespacet &ns)
14108
{
15-
// TODO: Implement expr2stl.
16-
// Expand this section so that it is able to properly convert from
17-
// CBMC expressions to STL code.
18-
return expr2c(expr, ns);
109+
expr2stlt expr2stl{ns};
110+
111+
return expr2stl.convert(expr);
19112
}
20113

21114
std::string type2stl(const typet &type, const namespacet &ns)
@@ -25,3 +118,187 @@ std::string type2stl(const typet &type, const namespacet &ns)
25118
// CBMC types to STL code.
26119
return type2c(type, ns);
27120
}
121+
122+
expr2stlt::expr2stlt(const namespacet &ns)
123+
: inside_bit_string(false), is_reference(false), ns(ns)
124+
{
125+
}
126+
127+
std::string expr2stlt::convert(const exprt &expr)
128+
{
129+
// Redirect to expr2c if no boolean expression.
130+
if(is_not_bool(expr))
131+
return expr2c(expr, ns);
132+
133+
if(ID_and == expr.id())
134+
convert(to_and_expr(expr));
135+
else if(ID_or == expr.id())
136+
convert(to_or_expr(expr));
137+
else if(ID_xor == expr.id())
138+
convert(to_xor_expr(expr));
139+
else if(ID_notequal == expr.id())
140+
convert(to_notequal_expr(expr));
141+
else if(ID_equal == expr.id())
142+
convert(to_equal_expr(expr));
143+
else if(ID_symbol == expr.id())
144+
convert(to_symbol_expr(expr));
145+
else if(ID_not == expr.id() && expr.op0().type().id() == ID_bool)
146+
convert(to_not_expr(expr));
147+
else // TODO: support more instructions in expr2stl.
148+
return expr2c(expr, ns);
149+
150+
return result.str();
151+
}
152+
153+
void expr2stlt::convert(const and_exprt &expr)
154+
{
155+
std::vector<exprt> operands = expr.operands();
156+
convert_multiary_bool(operands, AND);
157+
}
158+
159+
void expr2stlt::convert(const or_exprt &expr)
160+
{
161+
std::vector<exprt> operands = expr.operands();
162+
convert_multiary_bool(operands, OR);
163+
}
164+
165+
void expr2stlt::convert(const xor_exprt &expr)
166+
{
167+
std::vector<exprt> operands = expr.operands();
168+
convert_multiary_bool(operands, XOR);
169+
}
170+
171+
void expr2stlt::convert(const notequal_exprt &expr)
172+
{
173+
std::vector<exprt> operands = expr.operands();
174+
convert_multiary_bool(operands, XOR);
175+
}
176+
177+
void expr2stlt::convert(const equal_exprt &expr)
178+
{
179+
std::vector<exprt> operands =
180+
instrument_equal_operands(expr.lhs(), expr.rhs());
181+
convert_multiary_bool(operands, XOR);
182+
}
183+
184+
void expr2stlt::convert(const not_exprt &expr)
185+
{
186+
// If a NOT expression is being handled here it must always mark the
187+
// beginning of a new bit string.
188+
PRECONDITION(!inside_bit_string);
189+
190+
if(ID_symbol == expr.op().id())
191+
{
192+
// Use AN to load the symbol.
193+
is_reference = true;
194+
result << AND << NOT_POSTFIX << OPERAND_SEPARATOR;
195+
convert(to_symbol_expr(expr.op()));
196+
result << LINE_SEPARATOR;
197+
}
198+
else
199+
{
200+
// Use NOT to negate the RLO after the wrapped expression was loaded.
201+
convert(expr.op());
202+
result << NOT LINE_SEPARATOR;
203+
}
204+
}
205+
206+
void expr2stlt::convert(const symbol_exprt &expr)
207+
{
208+
if(is_reference)
209+
{
210+
result << REFERENCE_FLAG;
211+
is_reference = false;
212+
}
213+
result << id2string(id_shorthand(expr.get_identifier()));
214+
}
215+
216+
void expr2stlt::convert_multiary_bool(
217+
std::vector<exprt> &operands,
218+
const char operation)
219+
{
220+
if(inside_bit_string)
221+
convert_multiary_bool_operands(operands, operation);
222+
else
223+
{
224+
convert_first_non_trivial_operand(operands);
225+
convert_multiary_bool_operands(operands, operation);
226+
}
227+
}
228+
229+
void expr2stlt::convert_multiary_bool_operands(
230+
const std::vector<exprt> &operands,
231+
const char operation)
232+
{
233+
for(const exprt &op : operands)
234+
{
235+
if(ID_not == op.id())
236+
{
237+
result << operation << NOT_POSTFIX;
238+
convert_bool_operand(op.op0());
239+
}
240+
else
241+
{
242+
result << operation;
243+
convert_bool_operand(op);
244+
}
245+
}
246+
}
247+
248+
void expr2stlt::convert_bool_operand(const exprt &op)
249+
{
250+
if(op.id() == ID_symbol)
251+
{
252+
is_reference = true;
253+
result << OPERAND_SEPARATOR;
254+
convert(to_symbol_expr(op));
255+
result << LINE_SEPARATOR;
256+
}
257+
else
258+
{
259+
inside_bit_string = false;
260+
result << NESTING_OPEN_LINE_SEPARATOR;
261+
convert(op);
262+
result << NESTING_CLOSED_LINE_SEPARATOR;
263+
}
264+
}
265+
266+
void expr2stlt::convert_first_non_trivial_operand(std::vector<exprt> &operands)
267+
{
268+
exprt non_trivial_op;
269+
for(auto it{begin(operands)}; it != end(operands); ++it)
270+
{
271+
if(
272+
(ID_symbol == it->id()) ||
273+
(ID_not == it->id() && ID_symbol == it->op0().id()))
274+
continue;
275+
else
276+
{
277+
non_trivial_op = *it;
278+
operands.erase(it);
279+
break;
280+
}
281+
}
282+
// Important for some scenarios: Convert complex op first, set bit string
283+
// flag to true afterwards.
284+
if(non_trivial_op.is_not_nil())
285+
convert(non_trivial_op);
286+
287+
inside_bit_string = true;
288+
}
289+
290+
irep_idt expr2stlt::id_shorthand(const irep_idt &identifier)
291+
{
292+
const symbolt *symbol;
293+
std::string shorthand = id2string(identifier);
294+
if(
295+
!ns.lookup(identifier, symbol) && !symbol->base_name.empty() &&
296+
has_suffix(shorthand, id2string(symbol->base_name)))
297+
return symbol->base_name;
298+
299+
const std::string::size_type pos = shorthand.rfind(SCOPE_SEPARATOR);
300+
if(pos != std::string::npos)
301+
shorthand.erase(0, pos + std::strlen(SCOPE_SEPARATOR));
302+
303+
return shorthand;
304+
}

0 commit comments

Comments
 (0)