|
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