Skip to content

Commit 13f9785

Browse files
committed
Allow simplification of singleton interval.
Before this change, an expression like `value >= 255 && value <= 255` would fail to be simplified to `value == 255` by the expression simplifier. While the simplification itself is useful, the *lack* of it was causing some misbehaviour in one of our backends (the old SMT one under `src/solvers/smt2`) because the lack of simplification resulted in less constraints being propagated to the solver, missing some assertions around the expression that was using `value` as the size of an array.
1 parent 5d80835 commit 13f9785

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

src/util/simplify_expr_boolean.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
9999
bool no_change = true;
100100
bool may_be_reducible_to_interval =
101101
expr.id() == ID_or && expr.operands().size() > 2;
102+
bool may_be_reducible_to_singleton_interval =
103+
expr.id() == ID_and && expr.operands().size() == 2;
102104

103105
exprt::operandst new_operands = expr.operands();
104106

@@ -137,6 +139,103 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
137139
}
138140
}
139141

142+
// NOLINTNEXTLINE(whitespace/line_length)
143+
// This block reduces singleton intervals like (value >= 255 && value <= 255)
144+
// to just (value == 255). We also need to be careful with the operands
145+
// as some of them are erased in the previous step. We proceed only if
146+
// no operands have been erased (i.e. the expression structure has been
147+
// preserved by previous simplification rounds.)
148+
if(may_be_reducible_to_singleton_interval && new_operands.size() == 2)
149+
{
150+
struct boundst
151+
{
152+
mp_integer lower;
153+
mp_integer higher;
154+
};
155+
boundst bounds;
156+
bool structure_matched = false;
157+
158+
// Before we do anything else, we need to "pattern match" against the
159+
// expression and make sure that it has the structure we're looking for.
160+
// The structure we're looking for is going to be
161+
// (value >= 255 && !(value >= 256)) -- 255, 256 values indicative.
162+
// (this is because previous simplification runs will have transformed
163+
// the less_than_or_equal expression to a not(greater_than_or_equal)
164+
// expression)
165+
166+
// matching (value >= 255)
167+
auto const match_first_operand = [&bounds](const exprt &op) -> bool {
168+
if(
169+
const auto ge_expr =
170+
expr_try_dynamic_cast<greater_than_or_equal_exprt>(op))
171+
{
172+
// The construction of these expressions ensures that the RHS
173+
// is constant, therefore if we don't have a constant, it's a
174+
// different expression, so we bail.
175+
if(!ge_expr->rhs().is_constant())
176+
return false;
177+
if(
178+
auto int_opt =
179+
numeric_cast<mp_integer>(to_constant_expr(ge_expr->rhs())))
180+
{
181+
bounds.lower = *int_opt;
182+
return true;
183+
}
184+
return false;
185+
}
186+
return false;
187+
};
188+
189+
// matching !(value >= 256)
190+
auto const match_second_operand = [&bounds](const exprt &op) -> bool {
191+
if(const auto not_expr = expr_try_dynamic_cast<not_exprt>(op))
192+
{
193+
PRECONDITION(not_expr->operands().size() == 1);
194+
if(
195+
const auto ge_expr =
196+
expr_try_dynamic_cast<greater_than_or_equal_exprt>(
197+
not_expr->op()))
198+
{
199+
// If the rhs() is not constant, it has a different structure
200+
// (e.g. i >= j)
201+
if(!ge_expr->rhs().is_constant())
202+
return false;
203+
if(
204+
auto int_opt =
205+
numeric_cast<mp_integer>(to_constant_expr(ge_expr->rhs())))
206+
{
207+
bounds.higher = *int_opt - 1;
208+
return true;
209+
}
210+
return false;
211+
}
212+
return false;
213+
}
214+
return false;
215+
};
216+
217+
// We need to match both operands, at the particular sequence we expect.
218+
structure_matched |= match_first_operand(new_operands[0]);
219+
structure_matched &= match_second_operand(new_operands[1]);
220+
221+
if(structure_matched && bounds.lower == bounds.higher)
222+
{
223+
// Go through the expression again and convert >= operand into ==
224+
for(const auto &op : new_operands)
225+
{
226+
if(
227+
const auto ge_expr =
228+
expr_try_dynamic_cast<greater_than_or_equal_exprt>(op))
229+
{
230+
equal_exprt new_expr{ge_expr->lhs(), ge_expr->rhs()};
231+
return changed(new_expr);
232+
}
233+
else
234+
continue;
235+
}
236+
}
237+
}
238+
140239
if(may_be_reducible_to_interval)
141240
{
142241
optionalt<symbol_exprt> symbol_opt;

0 commit comments

Comments
 (0)