|
| 1 | +//! A pass that makes `SwitchInt`-on-`const` more obvious to later code. |
| 2 | +
|
| 3 | +use rustc_middle::bug; |
| 4 | +use rustc_middle::mir::*; |
| 5 | +use rustc_middle::ty::TyCtxt; |
| 6 | + |
| 7 | +/// A `MirPass` for simplifying `if T::CONST`. |
| 8 | +/// |
| 9 | +/// Today, MIR building for things like `if T::IS_ZST` introduce a constant |
| 10 | +/// for the copy of the bool, so it ends up in MIR as |
| 11 | +/// `_1 = CONST; switchInt (move _1)` or `_2 = CONST; switchInt (_2)`. |
| 12 | +/// |
| 13 | +/// This pass is very specifically targeted at *exactly* those patterns. |
| 14 | +/// It can absolutely be replaced with a more general pass should we get one that |
| 15 | +/// we can run in low optimization levels, but at the time of writing even in |
| 16 | +/// optimized builds this wasn't simplified. |
| 17 | +#[derive(Default)] |
| 18 | +pub struct SwitchConst; |
| 19 | + |
| 20 | +impl<'tcx> MirPass<'tcx> for SwitchConst { |
| 21 | + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
| 22 | + for block in body.basic_blocks.as_mut_preserves_cfg() { |
| 23 | + let switch_local = if let TerminatorKind::SwitchInt { discr, .. } = |
| 24 | + &block.terminator().kind |
| 25 | + && let Some(place) = discr.place() |
| 26 | + && let Some(local) = place.as_local() |
| 27 | + { |
| 28 | + local |
| 29 | + } else { |
| 30 | + continue; |
| 31 | + }; |
| 32 | + |
| 33 | + let new_operand = if let Some(statement) = block.statements.last() |
| 34 | + && let StatementKind::Assign(place_and_rvalue) = &statement.kind |
| 35 | + && let Some(local) = place_and_rvalue.0.as_local() |
| 36 | + && local == switch_local |
| 37 | + && let Rvalue::Use(operand) = &place_and_rvalue.1 |
| 38 | + && let Operand::Constant(_) = operand |
| 39 | + { |
| 40 | + operand.clone() |
| 41 | + } else { |
| 42 | + continue; |
| 43 | + }; |
| 44 | + |
| 45 | + if !tcx.consider_optimizing(|| format!("SwitchConst: switchInt(move {switch_local:?}")) |
| 46 | + { |
| 47 | + break; |
| 48 | + } |
| 49 | + |
| 50 | + let TerminatorKind::SwitchInt { discr, .. } = &mut block.terminator_mut().kind else { |
| 51 | + bug!("Somehow wasn't a switchInt any more?") |
| 52 | + }; |
| 53 | + *discr = new_operand; |
| 54 | + } |
| 55 | + } |
| 56 | +} |
0 commit comments