@@ -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,103 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
137
139
}
138
140
}
139
141
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
+
140
239
if (may_be_reducible_to_interval)
141
240
{
142
241
optionalt<symbol_exprt> symbol_opt;
0 commit comments