|
6 | 6 |
|
7 | 7 | \*******************************************************************/
|
8 | 8 |
|
9 |
| -#include "boolbv.h" |
10 |
| - |
11 | 9 | #include <util/arith_tools.h>
|
12 | 10 | #include <util/expr_util.h>
|
13 | 11 | #include <util/invariant.h>
|
14 | 12 | #include <util/simplify_expr.h>
|
| 13 | +#include <util/ssa_expr.h> |
| 14 | + |
| 15 | +#include "boolbv.h" |
15 | 16 |
|
16 | 17 | /// A method to detect equivalence between experts that can contain typecast
|
17 | 18 | static bool expr_eq(const exprt &expr1, const exprt &expr2)
|
@@ -280,12 +281,119 @@ literalt boolbvt::convert_quantifier(const quantifier_exprt &src)
|
280 | 281 | return quantifier_list.back().l;
|
281 | 282 | }
|
282 | 283 |
|
283 |
| -void boolbvt::finish_eager_conversion_quantifiers() |
| 284 | +/// Eliminate the quantifier in \p q_expr via E-matching in \p context_map. |
| 285 | +/// \return Quantifier-free expression, if quantifier elimination was |
| 286 | +/// successful, else nullopt. |
| 287 | +static std::optional<exprt> finish_one_quantifier( |
| 288 | + const quantifier_exprt &q_expr, |
| 289 | + const std::unordered_map<const exprt, bvt, irep_hash> &context_map) |
284 | 290 | {
|
285 |
| - if(quantifier_list.empty()) |
286 |
| - return; |
| 291 | + if(q_expr.variables().size() > 1) |
| 292 | + { |
| 293 | + // Rewrite Qx,y.P(x,y) as Qy.Qx.P(x,y), just like |
| 294 | + // eager_quantifier_instantiation does. |
| 295 | + auto new_variables = q_expr.variables(); |
| 296 | + new_variables.pop_back(); |
| 297 | + quantifier_exprt new_expression{ |
| 298 | + q_expr.id(), |
| 299 | + q_expr.variables().back(), |
| 300 | + quantifier_exprt{q_expr.id(), new_variables, q_expr.where()}}; |
| 301 | + return finish_one_quantifier(new_expression, context_map); |
| 302 | + } |
| 303 | + |
| 304 | + // find the contexts in which the bound variable is used |
| 305 | + const irep_idt &bound_variable_id = q_expr.symbol().get_identifier(); |
| 306 | + bool required_context = false; |
| 307 | + std::unordered_set<index_exprt, irep_hash> index_contexts; |
| 308 | + auto context_finder = |
| 309 | + [&bound_variable_id, &required_context, &index_contexts](const exprt &e) { |
| 310 | + if(auto symbol_expr = expr_try_dynamic_cast<symbol_exprt>(e)) |
| 311 | + { |
| 312 | + required_context |= bound_variable_id == symbol_expr->get_identifier(); |
| 313 | + } |
| 314 | + else if(required_context) |
| 315 | + { |
| 316 | + if(auto index_expr = expr_try_dynamic_cast<index_exprt>(e)) |
| 317 | + { |
| 318 | + index_contexts.insert(*index_expr); |
| 319 | + required_context = false; |
| 320 | + } |
| 321 | + } |
| 322 | + }; |
| 323 | + q_expr.where().visit_post(context_finder); |
| 324 | + // make sure we found some context for instantiation |
| 325 | + if(index_contexts.empty()) |
| 326 | + return {}; |
| 327 | + |
| 328 | + // match the contexts against expressions that we have cached |
| 329 | + std::unordered_set<exprt, irep_hash> instantiation_candidates; |
| 330 | + for(const auto &cache_entry : context_map) |
| 331 | + { |
| 332 | + // consider re-organizing the cache to use expression ids at the top level |
| 333 | + if(auto index_expr = expr_try_dynamic_cast<index_exprt>(cache_entry.first)) |
| 334 | + { |
| 335 | + for(const auto &index_context : index_contexts) |
| 336 | + { |
| 337 | + if( |
| 338 | + auto ssa_context = |
| 339 | + expr_try_dynamic_cast<ssa_exprt>(index_context.array())) |
| 340 | + { |
| 341 | + if( |
| 342 | + auto ssa_array = |
| 343 | + expr_try_dynamic_cast<ssa_exprt>(index_expr->array())) |
| 344 | + { |
| 345 | + if( |
| 346 | + ssa_context->get_l1_object_identifier() == |
| 347 | + ssa_array->get_l1_object_identifier()) |
| 348 | + { |
| 349 | + instantiation_candidates.insert(index_expr->index()); |
| 350 | + break; |
| 351 | + } |
| 352 | + } |
| 353 | + } |
| 354 | + else if(index_expr->array() == index_context.array()) |
| 355 | + { |
| 356 | + instantiation_candidates.insert(index_expr->index()); |
| 357 | + break; |
| 358 | + } |
| 359 | + } |
| 360 | + } |
| 361 | + } |
| 362 | + |
| 363 | + if(instantiation_candidates.empty()) |
| 364 | + return {}; |
| 365 | + |
| 366 | + exprt::operandst instantiations; |
| 367 | + instantiations.reserve(instantiation_candidates.size()); |
| 368 | + for(const auto &e : instantiation_candidates) |
| 369 | + { |
| 370 | + exprt::operandst values{ |
| 371 | + {typecast_exprt::conditional_cast(e, q_expr.symbol().type())}}; |
| 372 | + instantiations.push_back(q_expr.instantiate(values)); |
| 373 | + } |
287 | 374 |
|
288 |
| - // we do not yet have any elaborate post-processing |
| 375 | + if(q_expr.id() == ID_exists) |
| 376 | + return disjunction(instantiations); |
| 377 | + else |
| 378 | + return conjunction(instantiations); |
| 379 | +} |
| 380 | + |
| 381 | +void boolbvt::finish_eager_conversion_quantifiers() |
| 382 | +{ |
289 | 383 | for(const auto &q : quantifier_list)
|
290 |
| - conversion_failed(q.expr); |
| 384 | + { |
| 385 | + auto result_opt = |
| 386 | + finish_one_quantifier(to_quantifier_expr(q.expr), bv_cache); |
| 387 | + if(!result_opt.has_value()) |
| 388 | + { |
| 389 | + conversion_failed(q.expr); |
| 390 | + continue; |
| 391 | + } |
| 392 | + |
| 393 | + // Nested quantifiers may yield additional entries in quantifier_list via |
| 394 | + // convert; the range-for remains safe to use as long as quantifier_list is |
| 395 | + // a std::list. |
| 396 | + literalt result_lit = convert(*result_opt); |
| 397 | + prop.l_set_to_true(prop.lequal(q.l, result_lit)); |
| 398 | + } |
291 | 399 | }
|
0 commit comments