1 //! This pass optimizes the following sequence
2 //! ```rust,ignore (example)
9 //! switchInt(_2) -> [false: bb4, otherwise: bb5];
13 //! ```rust,ignore (example)
20 use crate::transform::MirPass;
21 use rustc_middle::mir::*;
22 use rustc_middle::ty::TyCtxt;
23 use rustc_middle::{mir::visit::Visitor, ty::ParamEnv};
25 use super::simplify::{simplify_cfg, simplify_locals};
29 impl<'tcx> MirPass<'tcx> for ConstGoto {
30 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
31 if tcx.sess.opts.debugging_opts.mir_opt_level < 3 {
34 trace!("Running ConstGoto on {:?}", body.source);
35 let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
37 ConstGotoOptimizationFinder { tcx, body, optimizations: vec![], param_env };
38 opt_finder.visit_body(body);
39 let should_simplify = !opt_finder.optimizations.is_empty();
40 for opt in opt_finder.optimizations {
41 let terminator = body.basic_blocks_mut()[opt.bb_with_goto].terminator_mut();
42 let new_goto = TerminatorKind::Goto { target: opt.target_to_use_in_goto };
43 debug!("SUCCESS: replacing `{:?}` with `{:?}`", terminator.kind, new_goto);
44 terminator.kind = new_goto;
47 // if we applied optimizations, we potentially have some cfg to cleanup to
48 // make it easier for further passes
51 simplify_locals(body, tcx);
56 impl<'a, 'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'a, 'tcx> {
57 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
58 let _: Option<_> = try {
59 let target = terminator.kind.as_goto()?;
60 // We only apply this optimization if the last statement is a const assignment
61 let last_statement = self.body.basic_blocks()[location.block].statements.last()?;
63 if let (place, Rvalue::Use(Operand::Constant(_const))) =
64 last_statement.kind.as_assign()?
66 // We found a constant being assigned to `place`.
67 // Now check that the target of this Goto switches on this place.
68 let target_bb = &self.body.basic_blocks()[target];
70 // FIXME(simonvandel): We are conservative here when we don't allow
71 // any statements in the target basic block.
72 // This could probably be relaxed to allow `StorageDead`s which could be
73 // copied to the predecessor of this block.
74 if !target_bb.statements.is_empty() {
78 let target_bb_terminator = target_bb.terminator();
79 let (discr, switch_ty, targets) = target_bb_terminator.kind.as_switch()?;
80 if discr.place() == Some(*place) {
81 // We now know that the Switch matches on the const place, and it is statementless
82 // Now find which value in the Switch matches the const value.
84 _const.literal.try_eval_bits(self.tcx, self.param_env, switch_ty)?;
85 let found_value_idx_option = targets
88 .find(|(_, (value, _))| const_value == *value)
91 let target_to_use_in_goto =
92 if let Some(found_value_idx) = found_value_idx_option {
93 targets.iter().nth(found_value_idx).unwrap().1
95 // If we did not find the const value in values, it must be the otherwise case
99 self.optimizations.push(OptimizationToApply {
100 bb_with_goto: location.block,
101 target_to_use_in_goto,
108 self.super_terminator(terminator, location);
112 struct OptimizationToApply {
113 bb_with_goto: BasicBlock,
114 target_to_use_in_goto: BasicBlock,
117 pub struct ConstGotoOptimizationFinder<'a, 'tcx> {
119 body: &'a Body<'tcx>,
120 param_env: ParamEnv<'tcx>,
121 optimizations: Vec<OptimizationToApply>,