|
17 | 17 | #include <util/expr_util.h>
|
18 | 18 | #include <util/invariant.h>
|
19 | 19 | #include <util/pointer_offset_size.h>
|
20 |
| -#include <util/range.h> |
21 | 20 | #include <util/std_expr.h>
|
22 | 21 |
|
23 | 22 | #include <analyses/dirty.h>
|
| 23 | +#include <util/simplify_expr.h> |
24 | 24 |
|
25 | 25 | void goto_symext::symex_goto(statet &state)
|
26 | 26 | {
|
@@ -353,138 +353,194 @@ void goto_symext::merge_value_sets(
|
353 | 353 | dest.value_set.make_union(src.value_set);
|
354 | 354 | }
|
355 | 355 |
|
356 |
| -void goto_symext::phi_function( |
357 |
| - const statet::goto_statet &goto_state, |
358 |
| - statet &dest_state) |
| 356 | +/// Applies f to `(k, ssa, i, j)` if the first map maps k to (ssa, i) and |
| 357 | +/// the second to (ssa', j). If the first map has an entry for k but not the |
| 358 | +/// second one then j is 0, and when the first map has no entry for k then i = 0 |
| 359 | +static void for_each2( |
| 360 | + const std::map<irep_idt, std::pair<ssa_exprt, unsigned>> &first_map, |
| 361 | + const std::map<irep_idt, std::pair<ssa_exprt, unsigned>> &second_map, |
| 362 | + const std::function<void(const ssa_exprt &, unsigned, unsigned)> &f) |
359 | 363 | {
|
360 |
| - auto ssa_of_current_name = |
361 |
| - [&](const std::pair<irep_idt, std::pair<ssa_exprt, unsigned>> &pair) { |
362 |
| - return pair.second.first; |
363 |
| - }; |
364 |
| - |
365 |
| - auto dest_state_names_range = |
366 |
| - make_range(dest_state.level2.current_names) |
367 |
| - .filter( |
368 |
| - [&](const std::pair<irep_idt, std::pair<ssa_exprt, unsigned>> &pair) { |
369 |
| - // We ignore the identifiers that are already in goto_state names |
370 |
| - return goto_state.level2_current_names.count(pair.first) == 0; |
371 |
| - }) |
372 |
| - .map<const ssa_exprt>(ssa_of_current_name); |
373 |
| - |
374 |
| - // go over all variables to see what changed |
375 |
| - auto all_current_names_range = make_range(goto_state.level2_current_names) |
376 |
| - .map<const ssa_exprt>(ssa_of_current_name) |
377 |
| - .concat(dest_state_names_range); |
378 |
| - |
379 |
| - guardt diff_guard; |
380 |
| - if(!all_current_names_range.empty()) |
| 364 | + auto second_it = second_map.begin(); |
| 365 | + for(const auto &first_pair : first_map) |
381 | 366 | {
|
382 |
| - diff_guard=goto_state.guard; |
383 |
| - |
384 |
| - // this gets the diff between the guards |
385 |
| - diff_guard-=dest_state.guard; |
| 367 | + while(second_it != second_map.end() && second_it->first < first_pair.first) |
| 368 | + { |
| 369 | + f(second_it->second.first, 0, second_it->second.second); |
| 370 | + ++second_it; |
| 371 | + } |
| 372 | + const ssa_exprt &ssa = first_pair.second.first; |
| 373 | + const unsigned count = first_pair.second.second; |
| 374 | + if(second_it != second_map.end() && second_it->first == first_pair.first) |
| 375 | + { |
| 376 | + f(ssa, count, second_it->second.second); |
| 377 | + ++second_it; |
| 378 | + } |
| 379 | + else |
| 380 | + f(ssa, count, 0); |
386 | 381 | }
|
387 |
| - |
388 |
| - for(const auto &ssa : all_current_names_range) |
| 382 | + while(second_it != second_map.end()) |
389 | 383 | {
|
390 |
| - const irep_idt l1_identifier = ssa.get_identifier(); |
391 |
| - const irep_idt &obj_identifier = ssa.get_object_name(); |
| 384 | + f(second_it->second.first, 0, second_it->second.second); |
| 385 | + ++second_it; |
| 386 | + } |
| 387 | +} |
392 | 388 |
|
393 |
| - if(obj_identifier==guard_identifier) |
394 |
| - continue; // just a guard, don't bother |
| 389 | +/// Helper function for \c phi_function which merges the names of an identifier |
| 390 | +/// for two different states. |
| 391 | +/// \param goto_state: first state |
| 392 | +/// \param[in, out] dest_state: second state |
| 393 | +/// \param ns: namespace |
| 394 | +/// \param diff_guard: difference between the guards of the two states |
| 395 | +/// \param guard_identifier: prefix used for goto symex guards |
| 396 | +/// \param[out] log: logger for debug messages |
| 397 | +/// \param do_simplify: should the right-hand-side of the assignment that is |
| 398 | +/// added to the target be simplified |
| 399 | +/// \param[out] target: equation that will receive the resulting assignment |
| 400 | +/// \param ssa: SSA expression to merge |
| 401 | +/// \param goto_count: current level 2 count in \p goto_state of |
| 402 | +/// \p l1_identifier |
| 403 | +/// \param dest_count: level 2 count in \p dest_state of \p l1_identifier |
| 404 | +static void merge_names( |
| 405 | + const goto_symext::statet::goto_statet &goto_state, |
| 406 | + goto_symext::statet &dest_state, |
| 407 | + const namespacet &ns, |
| 408 | + const guardt &diff_guard, |
| 409 | + const irep_idt &guard_identifier, |
| 410 | + messaget &log, |
| 411 | + const bool do_simplify, |
| 412 | + symex_target_equationt &target, |
| 413 | + const ssa_exprt &ssa, |
| 414 | + const unsigned goto_count, |
| 415 | + const unsigned dest_count) |
| 416 | +{ |
| 417 | + const irep_idt l1_identifier = ssa.get_identifier(); |
| 418 | + const irep_idt &obj_identifier = ssa.get_object_name(); |
395 | 419 |
|
396 |
| - if(goto_state.level2_current_count(l1_identifier)== |
397 |
| - dest_state.level2.current_count(l1_identifier)) |
398 |
| - continue; // not at all changed |
| 420 | + if(obj_identifier == guard_identifier) |
| 421 | + return; // just a guard, don't bother |
399 | 422 |
|
400 |
| - // changed! |
| 423 | + if(goto_count == dest_count) |
| 424 | + return; // not at all changed |
401 | 425 |
|
402 |
| - // shared variables are renamed on every access anyway, we don't need to |
403 |
| - // merge anything |
404 |
| - const symbolt &symbol=ns.lookup(obj_identifier); |
| 426 | + // changed! |
405 | 427 |
|
406 |
| - // shared? |
407 |
| - if( |
408 |
| - dest_state.atomic_section_id == 0 && dest_state.threads.size() >= 2 && |
409 |
| - (symbol.is_shared() || (dest_state.dirty)(symbol.name))) |
410 |
| - continue; // no phi nodes for shared stuff |
411 |
| - |
412 |
| - // don't merge (thread-)locals across different threads, which |
413 |
| - // may have been introduced by symex_start_thread (and will |
414 |
| - // only later be removed from level2.current_names by pop_frame |
415 |
| - // once the thread is executed) |
416 |
| - if( |
417 |
| - !ssa.get_level_0().empty() && |
418 |
| - ssa.get_level_0() != std::to_string(dest_state.source.thread_nr)) |
419 |
| - continue; |
| 428 | + // shared variables are renamed on every access anyway, we don't need to |
| 429 | + // merge anything |
| 430 | + const symbolt &symbol = ns.lookup(obj_identifier); |
420 | 431 |
|
421 |
| - exprt goto_state_rhs = ssa, dest_state_rhs = ssa; |
| 432 | + // shared? |
| 433 | + if( |
| 434 | + dest_state.atomic_section_id == 0 && dest_state.threads.size() >= 2 && |
| 435 | + (symbol.is_shared() || (dest_state.dirty)(symbol.name))) |
| 436 | + return; // no phi nodes for shared stuff |
| 437 | + |
| 438 | + // don't merge (thread-)locals across different threads, which |
| 439 | + // may have been introduced by symex_start_thread (and will |
| 440 | + // only later be removed from level2.current_names by pop_frame |
| 441 | + // once the thread is executed) |
| 442 | + const irep_idt level_0 = ssa.get_level_0(); |
| 443 | + if(!level_0.empty() && level_0 != std::to_string(dest_state.source.thread_nr)) |
| 444 | + return; |
422 | 445 |
|
423 |
| - { |
424 |
| - const auto p_it = goto_state.propagation.find(l1_identifier); |
| 446 | + exprt goto_state_rhs = ssa, dest_state_rhs = ssa; |
425 | 447 |
|
426 |
| - if(p_it != goto_state.propagation.end()) |
427 |
| - goto_state_rhs=p_it->second; |
428 |
| - else |
429 |
| - to_ssa_expr(goto_state_rhs).set_level_2( |
430 |
| - goto_state.level2_current_count(l1_identifier)); |
431 |
| - } |
| 448 | + { |
| 449 | + const auto p_it = goto_state.propagation.find(l1_identifier); |
432 | 450 |
|
433 |
| - { |
434 |
| - const auto p_it = dest_state.propagation.find(l1_identifier); |
| 451 | + if(p_it != goto_state.propagation.end()) |
| 452 | + goto_state_rhs = p_it->second; |
| 453 | + else |
| 454 | + to_ssa_expr(goto_state_rhs).set_level_2(goto_count); |
| 455 | + } |
435 | 456 |
|
436 |
| - if(p_it != dest_state.propagation.end()) |
437 |
| - dest_state_rhs=p_it->second; |
438 |
| - else |
439 |
| - to_ssa_expr(dest_state_rhs).set_level_2( |
440 |
| - dest_state.level2.current_count(l1_identifier)); |
441 |
| - } |
| 457 | + { |
| 458 | + const auto p_it = dest_state.propagation.find(l1_identifier); |
442 | 459 |
|
443 |
| - exprt rhs; |
444 |
| - |
445 |
| - // Don't add a conditional to the assignment when: |
446 |
| - // 1. Either guard is false, so we can't follow that branch. |
447 |
| - // 2. Either identifier is of generation zero, and so hasn't been |
448 |
| - // initialized and therefor an invalid target. |
449 |
| - if(dest_state.guard.is_false()) |
450 |
| - rhs=goto_state_rhs; |
451 |
| - else if(goto_state.guard.is_false()) |
452 |
| - rhs=dest_state_rhs; |
453 |
| - else if(goto_state.level2_current_count(l1_identifier) == 0) |
454 |
| - { |
455 |
| - rhs = dest_state_rhs; |
456 |
| - } |
457 |
| - else if(dest_state.level2.current_count(l1_identifier) == 0) |
458 |
| - { |
459 |
| - rhs = goto_state_rhs; |
460 |
| - } |
| 460 | + if(p_it != dest_state.propagation.end()) |
| 461 | + dest_state_rhs = p_it->second; |
461 | 462 | else
|
462 |
| - { |
463 |
| - rhs=if_exprt(diff_guard.as_expr(), goto_state_rhs, dest_state_rhs); |
464 |
| - do_simplify(rhs); |
465 |
| - } |
| 463 | + to_ssa_expr(dest_state_rhs).set_level_2(dest_count); |
| 464 | + } |
466 | 465 |
|
467 |
| - ssa_exprt new_lhs = ssa; |
468 |
| - const bool record_events=dest_state.record_events; |
469 |
| - dest_state.record_events=false; |
470 |
| - dest_state.assignment(new_lhs, rhs, ns, true, true); |
471 |
| - dest_state.record_events=record_events; |
472 |
| - |
473 |
| - log.conditional_output( |
474 |
| - log.debug(), |
475 |
| - [this, &new_lhs](messaget::mstreamt &mstream) { |
476 |
| - mstream << "Assignment to " << new_lhs.get_identifier() |
477 |
| - << " [" << pointer_offset_bits(new_lhs.type(), ns).value_or(0) << " bits]" |
478 |
| - << messaget::eom; |
479 |
| - }); |
480 |
| - |
481 |
| - target.assignment( |
482 |
| - true_exprt(), |
483 |
| - new_lhs, new_lhs, new_lhs.get_original_expr(), |
484 |
| - rhs, |
485 |
| - dest_state.source, |
486 |
| - symex_targett::assignment_typet::PHI); |
| 466 | + exprt rhs; |
| 467 | + |
| 468 | + // Don't add a conditional to the assignment when: |
| 469 | + // 1. Either guard is false, so we can't follow that branch. |
| 470 | + // 2. Either identifier is of generation zero, and so hasn't been |
| 471 | + // initialized and therefor an invalid target. |
| 472 | + if(dest_state.guard.is_false()) |
| 473 | + rhs = goto_state_rhs; |
| 474 | + else if(goto_state.guard.is_false()) |
| 475 | + rhs = dest_state_rhs; |
| 476 | + else if(goto_count == 0) |
| 477 | + { |
| 478 | + rhs = dest_state_rhs; |
487 | 479 | }
|
| 480 | + else if(dest_count == 0) |
| 481 | + { |
| 482 | + rhs = goto_state_rhs; |
| 483 | + } |
| 484 | + else |
| 485 | + { |
| 486 | + rhs = if_exprt(diff_guard.as_expr(), goto_state_rhs, dest_state_rhs); |
| 487 | + if(do_simplify) |
| 488 | + simplify(rhs, ns); |
| 489 | + } |
| 490 | + |
| 491 | + ssa_exprt new_lhs = ssa; |
| 492 | + const bool record_events = dest_state.record_events; |
| 493 | + dest_state.record_events = false; |
| 494 | + dest_state.assignment(new_lhs, rhs, ns, true, true); |
| 495 | + dest_state.record_events = record_events; |
| 496 | + |
| 497 | + log.conditional_output( |
| 498 | + log.debug(), [ns, &new_lhs](messaget::mstreamt &mstream) { |
| 499 | + mstream << "Assignment to " << new_lhs.get_identifier() << " [" |
| 500 | + << pointer_offset_bits(new_lhs.type(), ns).value_or(0) << " bits]" |
| 501 | + << messaget::eom; |
| 502 | + }); |
| 503 | + |
| 504 | + target.assignment( |
| 505 | + true_exprt(), |
| 506 | + new_lhs, |
| 507 | + new_lhs, |
| 508 | + new_lhs.get_original_expr(), |
| 509 | + rhs, |
| 510 | + dest_state.source, |
| 511 | + symex_targett::assignment_typet::PHI); |
| 512 | +} |
| 513 | + |
| 514 | +void goto_symext::phi_function( |
| 515 | + const statet::goto_statet &goto_state, |
| 516 | + statet &dest_state) |
| 517 | +{ |
| 518 | + if( |
| 519 | + goto_state.level2_current_names.empty() && |
| 520 | + dest_state.level2.current_names.empty()) |
| 521 | + return; |
| 522 | + |
| 523 | + guardt diff_guard = goto_state.guard; |
| 524 | + // this gets the diff between the guards |
| 525 | + diff_guard -= dest_state.guard; |
| 526 | + |
| 527 | + for_each2( |
| 528 | + goto_state.level2_current_names, |
| 529 | + dest_state.level2.current_names, |
| 530 | + [&](const ssa_exprt &ssa, unsigned goto_count, unsigned dest_count) { |
| 531 | + merge_names( |
| 532 | + goto_state, |
| 533 | + dest_state, |
| 534 | + ns, |
| 535 | + diff_guard, |
| 536 | + guard_identifier, |
| 537 | + log, |
| 538 | + symex_config.simplify_opt, |
| 539 | + target, |
| 540 | + ssa, |
| 541 | + goto_count, |
| 542 | + dest_count); |
| 543 | + }); |
488 | 544 | }
|
489 | 545 |
|
490 | 546 | void goto_symext::loop_bound_exceeded(
|
|
0 commit comments