Skip to content

Commit a00ea8e

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 a00ea8e

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

src/util/simplify_expr_boolean.cpp

Lines changed: 100 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,104 @@ 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+
{
176+
return false;
177+
}
178+
if(
179+
auto int_opt =
180+
numeric_cast<mp_integer>(to_constant_expr(ge_expr->rhs())))
181+
{
182+
bounds.lower = *int_opt;
183+
return true;
184+
}
185+
return false;
186+
}
187+
else
188+
{
189+
return false;
190+
}
191+
};
192+
193+
// matching !(value >= 256)
194+
auto const match_second_operand = [&bounds](const exprt &op) -> bool
195+
{
196+
if(const auto not_expr = expr_try_dynamic_cast<not_exprt>(op))
197+
{
198+
PRECONDITION(not_expr->operands().size() == 1);
199+
if (const auto ge_expr = expr_try_dynamic_cast<greater_than_or_equal_exprt>(not_expr->op()))
200+
{
201+
// If the rhs() is not constant, it has a different structure (e.g. i >= j)
202+
if(!ge_expr->rhs().is_constant())
203+
{
204+
return false;
205+
}
206+
if (auto int_opt = numeric_cast<mp_integer>(to_constant_expr(ge_expr->rhs())))
207+
{
208+
bounds.higher = *int_opt - 1;
209+
return true;
210+
}
211+
return false;
212+
}
213+
else
214+
{
215+
return false;
216+
}
217+
}
218+
else
219+
{
220+
return false;
221+
}
222+
};
223+
224+
// We need to match both operands, at the particular sequence we expect.
225+
structure_matched |= match_first_operand(new_operands[0]);
226+
structure_matched &= match_second_operand(new_operands[1]);
227+
228+
if(structure_matched && bounds.lower == bounds.higher)
229+
{
230+
// Go through the expression again and convert >= operand into ==
231+
for (const auto &op : new_operands) {
232+
if (const auto ge_expr = expr_try_dynamic_cast<greater_than_or_equal_exprt>(op)) {
233+
equal_exprt new_expr{ge_expr->lhs(), ge_expr->rhs()};
234+
return changed(new_expr);
235+
} else continue;
236+
}
237+
}
238+
}
239+
140240
if(may_be_reducible_to_interval)
141241
{
142242
optionalt<symbol_exprt> symbol_opt;

0 commit comments

Comments
 (0)