|
9 | 9 | //!
|
10 | 10 | //! into just `x`.
|
11 | 11 |
|
12 |
| -use crate::transform::{simplify, MirPass}; |
13 |
| -use itertools::Itertools as _; |
| 12 | +use crate::transform::MirPass; |
14 | 13 | use rustc_index::{bit_set::BitSet, vec::IndexVec};
|
15 | 14 | use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
16 | 15 | use rustc_middle::mir::*;
|
17 |
| -use rustc_middle::ty::{self, List, Ty, TyCtxt}; |
| 16 | +use rustc_middle::ty::{List, Ty, TyCtxt}; |
18 | 17 | use rustc_target::abi::VariantIdx;
|
19 |
| -use std::iter::{once, Enumerate, Peekable}; |
| 18 | +use std::iter::{Enumerate, Peekable}; |
20 | 19 | use std::slice::Iter;
|
21 | 20 |
|
22 | 21 | /// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
|
@@ -523,265 +522,3 @@ fn match_variant_field_place<'tcx>(place: Place<'tcx>) -> Option<(Local, VarFiel
|
523 | 522 | _ => None,
|
524 | 523 | }
|
525 | 524 | }
|
526 |
| - |
527 |
| -/// Simplifies `SwitchInt(_) -> [targets]`, |
528 |
| -/// where all the `targets` have the same form, |
529 |
| -/// into `goto -> target_first`. |
530 |
| -pub struct SimplifyBranchSame; |
531 |
| - |
532 |
| -impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { |
533 |
| - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
534 |
| - trace!("Running SimplifyBranchSame on {:?}", body.source); |
535 |
| - let finder = SimplifyBranchSameOptimizationFinder { body, tcx }; |
536 |
| - let opts = finder.find(); |
537 |
| - |
538 |
| - let did_remove_blocks = opts.len() > 0; |
539 |
| - for opt in opts.iter() { |
540 |
| - trace!("SUCCESS: Applying optimization {:?}", opt); |
541 |
| - // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`. |
542 |
| - body.basic_blocks_mut()[opt.bb_to_opt_terminator].terminator_mut().kind = |
543 |
| - TerminatorKind::Goto { target: opt.bb_to_goto }; |
544 |
| - } |
545 |
| - |
546 |
| - if did_remove_blocks { |
547 |
| - // We have dead blocks now, so remove those. |
548 |
| - simplify::remove_dead_blocks(body); |
549 |
| - } |
550 |
| - } |
551 |
| -} |
552 |
| - |
553 |
| -#[derive(Debug)] |
554 |
| -struct SimplifyBranchSameOptimization { |
555 |
| - /// All basic blocks are equal so go to this one |
556 |
| - bb_to_goto: BasicBlock, |
557 |
| - /// Basic block where the terminator can be simplified to a goto |
558 |
| - bb_to_opt_terminator: BasicBlock, |
559 |
| -} |
560 |
| - |
561 |
| -struct SwitchTargetAndValue { |
562 |
| - target: BasicBlock, |
563 |
| - // None in case of the `otherwise` case |
564 |
| - value: Option<u128>, |
565 |
| -} |
566 |
| - |
567 |
| -struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> { |
568 |
| - body: &'a Body<'tcx>, |
569 |
| - tcx: TyCtxt<'tcx>, |
570 |
| -} |
571 |
| - |
572 |
| -impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { |
573 |
| - fn find(&self) -> Vec<SimplifyBranchSameOptimization> { |
574 |
| - self.body |
575 |
| - .basic_blocks() |
576 |
| - .iter_enumerated() |
577 |
| - .filter_map(|(bb_idx, bb)| { |
578 |
| - let (discr_switched_on, targets_and_values) = match &bb.terminator().kind { |
579 |
| - TerminatorKind::SwitchInt { targets, discr, values, .. } => { |
580 |
| - // if values.len() == targets.len() - 1, we need to include None where no value is present |
581 |
| - // such that the zip does not throw away targets. If no `otherwise` case is in targets, the zip will simply throw away the added None |
582 |
| - let values_extended = values.iter().map(|x|Some(*x)).chain(once(None)); |
583 |
| - let targets_and_values:Vec<_> = targets.iter().zip(values_extended) |
584 |
| - .map(|(target, value)| SwitchTargetAndValue{target:*target, value}) |
585 |
| - .collect(); |
586 |
| - assert_eq!(targets.len(), targets_and_values.len()); |
587 |
| - (discr, targets_and_values)}, |
588 |
| - _ => return None, |
589 |
| - }; |
590 |
| - |
591 |
| - // find the adt that has its discriminant read |
592 |
| - // assuming this must be the last statement of the block |
593 |
| - let adt_matched_on = match &bb.statements.last()?.kind { |
594 |
| - StatementKind::Assign(box (place, rhs)) |
595 |
| - if Some(*place) == discr_switched_on.place() => |
596 |
| - { |
597 |
| - match rhs { |
598 |
| - Rvalue::Discriminant(adt_place) if adt_place.ty(self.body, self.tcx).ty.is_enum() => adt_place, |
599 |
| - _ => { |
600 |
| - trace!("NO: expected a discriminant read of an enum instead of: {:?}", rhs); |
601 |
| - return None; |
602 |
| - } |
603 |
| - } |
604 |
| - } |
605 |
| - other => { |
606 |
| - trace!("NO: expected an assignment of a discriminant read to a place. Found: {:?}", other); |
607 |
| - return None |
608 |
| - }, |
609 |
| - }; |
610 |
| - |
611 |
| - let mut iter_bbs_reachable = targets_and_values |
612 |
| - .iter() |
613 |
| - .map(|target_and_value| (target_and_value, &self.body.basic_blocks()[target_and_value.target])) |
614 |
| - .filter(|(_, bb)| { |
615 |
| - // Reaching `unreachable` is UB so assume it doesn't happen. |
616 |
| - bb.terminator().kind != TerminatorKind::Unreachable |
617 |
| - // But `asm!(...)` could abort the program, |
618 |
| - // so we cannot assume that the `unreachable` terminator itself is reachable. |
619 |
| - // FIXME(Centril): use a normalization pass instead of a check. |
620 |
| - || bb.statements.iter().any(|stmt| match stmt.kind { |
621 |
| - StatementKind::LlvmInlineAsm(..) => true, |
622 |
| - _ => false, |
623 |
| - }) |
624 |
| - }) |
625 |
| - .peekable(); |
626 |
| - |
627 |
| - let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(&targets_and_values[0]); |
628 |
| - let mut all_successors_equivalent = StatementEquality::TrivialEqual; |
629 |
| - |
630 |
| - // All successor basic blocks must be equal or contain statements that are pairwise considered equal. |
631 |
| - for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() { |
632 |
| - let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup |
633 |
| - && bb_l.terminator().kind == bb_r.terminator().kind |
634 |
| - && bb_l.statements.len() == bb_r.statements.len(); |
635 |
| - let statement_check = || { |
636 |
| - bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| { |
637 |
| - let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r); |
638 |
| - if matches!(stmt_equality, StatementEquality::NotEqual) { |
639 |
| - // short circuit |
640 |
| - None |
641 |
| - } else { |
642 |
| - Some(acc.combine(&stmt_equality)) |
643 |
| - } |
644 |
| - }) |
645 |
| - .unwrap_or(StatementEquality::NotEqual) |
646 |
| - }; |
647 |
| - if !trivial_checks { |
648 |
| - all_successors_equivalent = StatementEquality::NotEqual; |
649 |
| - break; |
650 |
| - } |
651 |
| - all_successors_equivalent = all_successors_equivalent.combine(&statement_check()); |
652 |
| - }; |
653 |
| - |
654 |
| - match all_successors_equivalent{ |
655 |
| - StatementEquality::TrivialEqual => { |
656 |
| - // statements are trivially equal, so just take first |
657 |
| - trace!("Statements are trivially equal"); |
658 |
| - Some(SimplifyBranchSameOptimization { |
659 |
| - bb_to_goto: bb_first.target, |
660 |
| - bb_to_opt_terminator: bb_idx, |
661 |
| - }) |
662 |
| - } |
663 |
| - StatementEquality::ConsideredEqual(bb_to_choose) => { |
664 |
| - trace!("Statements are considered equal"); |
665 |
| - Some(SimplifyBranchSameOptimization { |
666 |
| - bb_to_goto: bb_to_choose, |
667 |
| - bb_to_opt_terminator: bb_idx, |
668 |
| - }) |
669 |
| - } |
670 |
| - StatementEquality::NotEqual => { |
671 |
| - trace!("NO: not all successors of basic block {:?} were equivalent", bb_idx); |
672 |
| - None |
673 |
| - } |
674 |
| - } |
675 |
| - }) |
676 |
| - .collect() |
677 |
| - } |
678 |
| - |
679 |
| - /// Tests if two statements can be considered equal |
680 |
| - /// |
681 |
| - /// Statements can be trivially equal if the kinds match. |
682 |
| - /// But they can also be considered equal in the following case A: |
683 |
| - /// ``` |
684 |
| - /// discriminant(_0) = 0; // bb1 |
685 |
| - /// _0 = move _1; // bb2 |
686 |
| - /// ``` |
687 |
| - /// In this case the two statements are equal iff |
688 |
| - /// 1: _0 is an enum where the variant index 0 is fieldless, and |
689 |
| - /// 2: bb1 was targeted by a switch where the discriminant of _1 was switched on |
690 |
| - fn statement_equality( |
691 |
| - &self, |
692 |
| - adt_matched_on: Place<'tcx>, |
693 |
| - x: &Statement<'tcx>, |
694 |
| - x_target_and_value: &SwitchTargetAndValue, |
695 |
| - y: &Statement<'tcx>, |
696 |
| - y_target_and_value: &SwitchTargetAndValue, |
697 |
| - ) -> StatementEquality { |
698 |
| - let helper = |rhs: &Rvalue<'tcx>, |
699 |
| - place: &Place<'tcx>, |
700 |
| - variant_index: &VariantIdx, |
701 |
| - side_to_choose| { |
702 |
| - let place_type = place.ty(self.body, self.tcx).ty; |
703 |
| - let adt = match *place_type.kind() { |
704 |
| - ty::Adt(adt, _) if adt.is_enum() => adt, |
705 |
| - _ => return StatementEquality::NotEqual, |
706 |
| - }; |
707 |
| - let variant_is_fieldless = adt.variants[*variant_index].fields.is_empty(); |
708 |
| - if !variant_is_fieldless { |
709 |
| - trace!("NO: variant {:?} was not fieldless", variant_index); |
710 |
| - return StatementEquality::NotEqual; |
711 |
| - } |
712 |
| - |
713 |
| - match rhs { |
714 |
| - Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => { |
715 |
| - StatementEquality::ConsideredEqual(side_to_choose) |
716 |
| - } |
717 |
| - _ => { |
718 |
| - trace!( |
719 |
| - "NO: RHS of assignment was {:?}, but expected it to match the adt being matched on in the switch, which is {:?}", |
720 |
| - rhs, |
721 |
| - adt_matched_on |
722 |
| - ); |
723 |
| - StatementEquality::NotEqual |
724 |
| - } |
725 |
| - } |
726 |
| - }; |
727 |
| - match (&x.kind, &y.kind) { |
728 |
| - // trivial case |
729 |
| - (x, y) if x == y => StatementEquality::TrivialEqual, |
730 |
| - |
731 |
| - // check for case A |
732 |
| - ( |
733 |
| - StatementKind::Assign(box (_, rhs)), |
734 |
| - StatementKind::SetDiscriminant { place, variant_index }, |
735 |
| - ) |
736 |
| - // we need to make sure that the switch value that targets the bb with SetDiscriminant (y), is the same as the variant index |
737 |
| - if Some(variant_index.index() as u128) == y_target_and_value.value => { |
738 |
| - // choose basic block of x, as that has the assign |
739 |
| - helper(rhs, place, variant_index, x_target_and_value.target) |
740 |
| - } |
741 |
| - ( |
742 |
| - StatementKind::SetDiscriminant { place, variant_index }, |
743 |
| - StatementKind::Assign(box (_, rhs)), |
744 |
| - ) |
745 |
| - // we need to make sure that the switch value that targets the bb with SetDiscriminant (x), is the same as the variant index |
746 |
| - if Some(variant_index.index() as u128) == x_target_and_value.value => { |
747 |
| - // choose basic block of y, as that has the assign |
748 |
| - helper(rhs, place, variant_index, y_target_and_value.target) |
749 |
| - } |
750 |
| - _ => { |
751 |
| - trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y); |
752 |
| - StatementEquality::NotEqual |
753 |
| - } |
754 |
| - } |
755 |
| - } |
756 |
| -} |
757 |
| - |
758 |
| -#[derive(Copy, Clone, Eq, PartialEq)] |
759 |
| -enum StatementEquality { |
760 |
| - /// The two statements are trivially equal; same kind |
761 |
| - TrivialEqual, |
762 |
| - /// The two statements are considered equal, but may be of different kinds. The BasicBlock field is the basic block to jump to when performing the branch-same optimization. |
763 |
| - /// For example, `_0 = _1` and `discriminant(_0) = discriminant(0)` are considered equal if 0 is a fieldless variant of an enum. But we don't want to jump to the basic block with the SetDiscriminant, as that is not legal if _1 is not the 0 variant index |
764 |
| - ConsideredEqual(BasicBlock), |
765 |
| - /// The two statements are not equal |
766 |
| - NotEqual, |
767 |
| -} |
768 |
| - |
769 |
| -impl StatementEquality { |
770 |
| - fn combine(&self, other: &StatementEquality) -> StatementEquality { |
771 |
| - use StatementEquality::*; |
772 |
| - match (self, other) { |
773 |
| - (TrivialEqual, TrivialEqual) => TrivialEqual, |
774 |
| - (TrivialEqual, ConsideredEqual(b)) | (ConsideredEqual(b), TrivialEqual) => { |
775 |
| - ConsideredEqual(*b) |
776 |
| - } |
777 |
| - (ConsideredEqual(b1), ConsideredEqual(b2)) => { |
778 |
| - if b1 == b2 { |
779 |
| - ConsideredEqual(*b1) |
780 |
| - } else { |
781 |
| - NotEqual |
782 |
| - } |
783 |
| - } |
784 |
| - (_, NotEqual) | (NotEqual, _) => NotEqual, |
785 |
| - } |
786 |
| - } |
787 |
| -} |
0 commit comments