@@ -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,104 @@ 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
+ {
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
+
140
240
if (may_be_reducible_to_interval)
141
241
{
142
242
optionalt<symbol_exprt> symbol_opt;
0 commit comments