|
26 | 26 | //! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we
|
27 | 27 | //! naively generate still contains the `_a = ()` write in the unreachable block "after" the
|
28 | 28 | //! return.
|
| 29 | +//! |
| 30 | +//! **WARNING**: `SimplifyCfg` is one of the few optimizations that runs on built and analysis |
| 31 | +//! MIR, and so its effects may affect the type-checking, borrow-checking, and other analysis. |
| 32 | +//! We must be extremely careful to only apply optimizations that preserve UB and all |
| 33 | +//! non-determinism, since changes here can affect which programs compile in an insta-stable way. |
| 34 | +//! The normal logic that a program with UB can be changed to do anything does not apply to |
| 35 | +//! pre-"runtime" MIR! |
29 | 36 |
|
30 | 37 | use rustc_index::{Idx, IndexSlice, IndexVec};
|
31 | 38 | use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
|
@@ -144,7 +151,6 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
144 | 151 | merged_blocks.clear();
|
145 | 152 | while inner_changed {
|
146 | 153 | inner_changed = false;
|
147 |
| - inner_changed |= self.simplify_branch(&mut terminator); |
148 | 154 | inner_changed |= self.merge_successor(&mut merged_blocks, &mut terminator);
|
149 | 155 | changed |= inner_changed;
|
150 | 156 | }
|
@@ -251,32 +257,6 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
|
251 | 257 | true
|
252 | 258 | }
|
253 | 259 |
|
254 |
| - // turn a branch with all successors identical to a goto |
255 |
| - fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { |
256 |
| - match terminator.kind { |
257 |
| - TerminatorKind::SwitchInt { .. } => {} |
258 |
| - _ => return false, |
259 |
| - }; |
260 |
| - |
261 |
| - let first_succ = { |
262 |
| - if let Some(first_succ) = terminator.successors().next() { |
263 |
| - if terminator.successors().all(|s| s == first_succ) { |
264 |
| - let count = terminator.successors().count(); |
265 |
| - self.pred_count[first_succ] -= (count - 1) as u32; |
266 |
| - first_succ |
267 |
| - } else { |
268 |
| - return false; |
269 |
| - } |
270 |
| - } else { |
271 |
| - return false; |
272 |
| - } |
273 |
| - }; |
274 |
| - |
275 |
| - debug!("simplifying branch {:?}", terminator); |
276 |
| - terminator.kind = TerminatorKind::Goto { target: first_succ }; |
277 |
| - true |
278 |
| - } |
279 |
| - |
280 | 260 | fn strip_nops(&mut self) {
|
281 | 261 | for blk in self.basic_blocks.iter_mut() {
|
282 | 262 | blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop))
|
@@ -615,3 +595,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
|
615 | 595 | *l = self.map[*l].unwrap();
|
616 | 596 | }
|
617 | 597 | }
|
| 598 | + |
| 599 | +pub struct RemoveRedundantSwitch; |
| 600 | + |
| 601 | +impl<'tcx> crate::MirPass<'tcx> for RemoveRedundantSwitch { |
| 602 | + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
| 603 | + loop { |
| 604 | + let mut should_simplify = false; |
| 605 | + |
| 606 | + for block in body.basic_blocks_mut() { |
| 607 | + let TerminatorKind::SwitchInt { discr: _, targets } = &block.terminator().kind |
| 608 | + else { |
| 609 | + continue; |
| 610 | + }; |
| 611 | + let Some((first_succ, rest)) = targets.all_targets().split_first() else { |
| 612 | + continue; |
| 613 | + }; |
| 614 | + if !rest.iter().all(|succ| succ == first_succ) { |
| 615 | + continue; |
| 616 | + } |
| 617 | + block.terminator_mut().kind = TerminatorKind::Goto { target: *first_succ }; |
| 618 | + should_simplify = true; |
| 619 | + } |
| 620 | + |
| 621 | + if should_simplify { |
| 622 | + simplify_cfg(body); |
| 623 | + } else { |
| 624 | + break; |
| 625 | + } |
| 626 | + } |
| 627 | + } |
| 628 | + |
| 629 | + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { |
| 630 | + sess.mir_opt_level() >= 1 |
| 631 | + } |
| 632 | + |
| 633 | + fn is_required(&self) -> bool { |
| 634 | + false |
| 635 | + } |
| 636 | +} |
0 commit comments