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
.mir_opt_level() < 4 {
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
>,