|
1 | 1 | //! This pass removes jumps to basic blocks containing only a return, and replaces them with a |
2 | 2 | //! return instead. |
3 | 3 |
|
4 | | -use rustc_index::bit_set::DenseBitSet; |
5 | 4 | use rustc_middle::mir::*; |
6 | 5 | use rustc_middle::ty::TyCtxt; |
7 | | - |
8 | | -use crate::simplify; |
| 6 | +use smallvec::SmallVec; |
9 | 7 |
|
10 | 8 | pub(super) struct MultipleReturnTerminators; |
11 | 9 |
|
12 | 10 | impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { |
13 | 11 | fn is_enabled(&self, sess: &rustc_session::Session) -> bool { |
14 | | - sess.mir_opt_level() >= 4 |
| 12 | + sess.mir_opt_level() >= 2 |
15 | 13 | } |
16 | 14 |
|
17 | 15 | fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
18 | | - // find basic blocks with no statement and a return terminator |
19 | | - let mut bbs_simple_returns = DenseBitSet::new_empty(body.basic_blocks.len()); |
20 | | - let bbs = body.basic_blocks_mut(); |
21 | | - for idx in bbs.indices() { |
22 | | - if bbs[idx].statements.is_empty() |
23 | | - && bbs[idx].terminator().kind == TerminatorKind::Return |
| 16 | + let mut to_handle = <Vec<(BasicBlock, SmallVec<_>)>>::new(); |
| 17 | + for (bb, bbdata) in body.basic_blocks.iter_enumerated() { |
| 18 | + // Look for returns where, if we lift them into the parents, we can save a block. |
| 19 | + if let TerminatorKind::Return = bbdata.terminator().kind |
| 20 | + && bbdata |
| 21 | + .statements |
| 22 | + .iter() |
| 23 | + .all(|stmt| matches!(stmt.kind, StatementKind::StorageDead(_))) |
| 24 | + && let predecessors = &body.basic_blocks.predecessors()[bb] |
| 25 | + && predecessors.len() >= 2 |
| 26 | + && predecessors.iter().all(|pred| { |
| 27 | + matches!( |
| 28 | + body.basic_blocks[*pred].terminator().kind, |
| 29 | + TerminatorKind::Goto { .. }, |
| 30 | + ) |
| 31 | + }) |
24 | 32 | { |
25 | | - bbs_simple_returns.insert(idx); |
| 33 | + to_handle.push((bb, predecessors.clone())); |
26 | 34 | } |
27 | 35 | } |
28 | 36 |
|
29 | | - for bb in bbs { |
30 | | - if let TerminatorKind::Goto { target } = bb.terminator().kind { |
31 | | - if bbs_simple_returns.contains(target) { |
32 | | - bb.terminator_mut().kind = TerminatorKind::Return; |
33 | | - } |
34 | | - } |
| 37 | + if to_handle.is_empty() { |
| 38 | + return; |
35 | 39 | } |
36 | 40 |
|
37 | | - simplify::remove_dead_blocks(body) |
| 41 | + let bbs = body.basic_blocks_mut(); |
| 42 | + for (succ, predecessors) in to_handle { |
| 43 | + for pred in predecessors { |
| 44 | + let (pred_block, succ_block) = bbs.pick2_mut(pred, succ); |
| 45 | + pred_block.statements.extend(succ_block.statements.iter().cloned()); |
| 46 | + *pred_block.terminator_mut() = succ_block.terminator().clone(); |
| 47 | + } |
| 48 | + } |
38 | 49 | } |
39 | 50 |
|
40 | 51 | fn is_required(&self) -> bool { |
|
0 commit comments