@@ -99,6 +99,8 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
99
99
bool no_change = true ;
100
100
bool may_be_reducible_to_interval =
101
101
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 ;
102
104
103
105
exprt::operandst new_operands = expr.operands ();
104
106
@@ -137,6 +139,102 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
137
139
}
138
140
}
139
141
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
+
140
238
if (may_be_reducible_to_interval)
141
239
{
142
240
optionalt<symbol_exprt> symbol_opt;
0 commit comments