Skip to content

Commit eeff129

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 0ca29d8 commit eeff129

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

src/util/simplify_expr_boolean.cpp

Lines changed: 98 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,102 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
137139
}
138140
}
139141

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

0 commit comments

Comments
 (0)