1- use crate :: transform:: { simplify , MirPass , MirSource } ;
1+ use crate :: transform:: { MirPass , MirSource } ;
22use rustc_middle:: mir:: * ;
33use rustc_middle:: ty:: TyCtxt ;
44
@@ -12,71 +12,63 @@ pub struct MatchBranchSimplification;
1212impl < ' tcx > MirPass < ' tcx > for MatchBranchSimplification {
1313 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , src : MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
1414 let param_env = tcx. param_env ( src. def_id ( ) ) ;
15- let mut did_remove_blocks = false ;
1615 let bbs = body. basic_blocks_mut ( ) ;
1716 ' outer: for bb_idx in bbs. indices ( ) {
18- let ( discr, val, switch_ty, targets ) = match bbs[ bb_idx] . terminator ( ) . kind {
17+ let ( discr, val, switch_ty, first , second ) = match bbs[ bb_idx] . terminator ( ) . kind {
1918 TerminatorKind :: SwitchInt {
2019 discr : Operand :: Move ( ref place) ,
2120 switch_ty,
2221 ref targets,
2322 ref values,
2423 ..
2524 } if targets. len ( ) == 2 && values. len ( ) == 1 => {
26- ( place. clone ( ) , values[ 0 ] , switch_ty, targets)
25+ ( place, values[ 0 ] , switch_ty, targets[ 0 ] , targets [ 1 ] )
2726 }
27+ // Only optimize switch int statements
2828 _ => continue ,
2929 } ;
30- let ( first, rest) = if let ( [ first] , rest) = targets. split_at ( 1 ) {
31- ( * first, rest)
32- } else {
33- unreachable ! ( ) ;
34- } ;
35- let first_dest = bbs[ first] . terminator ( ) . kind . clone ( ) ;
36- let same_destinations = rest
37- . iter ( )
38- . map ( |target| & bbs[ * target] . terminator ( ) . kind )
39- . all ( |t_kind| t_kind == & first_dest) ;
40- if !same_destinations {
30+
31+ // Check that destinations are identical, and if not, then don't optimize this block
32+ if & bbs[ first] . terminator ( ) . kind != & bbs[ second] . terminator ( ) . kind {
4133 continue ;
4234 }
35+
36+ // Check that blocks are assignments of consts to the same place or same statement,
37+ // and match up 1-1, if not don't optimize this block.
4338 let first_stmts = & bbs[ first] . statements ;
44- for s in first_stmts. iter ( ) {
45- match & s. kind {
46- StatementKind :: Assign ( box ( _, rhs) ) => {
47- if let Rvalue :: Use ( Operand :: Constant ( _) ) = rhs {
48- } else {
49- continue ' outer;
50- }
51- }
52- _ => continue ' outer,
53- }
39+ let scnd_stmts = & bbs[ second] . statements ;
40+ if first_stmts. len ( ) != scnd_stmts. len ( ) {
41+ continue ;
5442 }
55- for target in rest . iter ( ) {
56- for s in bbs [ * target ] . statements . iter ( ) {
57- if let StatementKind :: Assign ( box ( ref lhs , rhs ) ) = & s . kind {
58- if let Rvalue :: Use ( Operand :: Constant ( _ ) ) = rhs {
59- let has_matching_assn = first_stmts
60- . iter ( )
61- . find ( |s| {
62- if let StatementKind :: Assign ( box ( lhs_f , _ ) ) = & s . kind {
63- lhs_f == lhs
64- } else {
65- false
66- }
67- } )
68- . is_some ( ) ;
69- if has_matching_assn {
70- continue ;
43+ for ( f , s ) in first_stmts . iter ( ) . zip ( scnd_stmts . iter ( ) ) {
44+ match ( & f . kind , & s . kind ) {
45+ // If two statements are exactly the same just ignore them.
46+ ( f_s , s_s ) if f_s == s_s => ( ) ,
47+
48+ (
49+ StatementKind :: Assign ( box ( lhs_f , Rvalue :: Use ( Operand :: Constant ( f_c ) ) ) ) ,
50+ StatementKind :: Assign ( box ( lhs_s , Rvalue :: Use ( Operand :: Constant ( s_c ) ) ) ) ,
51+ ) if lhs_f == lhs_s => {
52+ if let Some ( f_c ) = f_c . literal . try_eval_bool ( tcx , param_env ) {
53+ // This should also be a bool because it's writing to the same place
54+ let s_c = s_c . literal . try_eval_bool ( tcx , param_env ) . unwrap ( ) ;
55+ // Check that only const assignments of opposite bool values are
56+ // permitted.
57+ if f_c != s_c {
58+ continue
7159 }
7260 }
61+ continue ' outer;
7362 }
74-
75- continue ' outer;
63+ // If there are not exclusively assignments, then ignore this
64+ _ => continue ' outer,
7665 }
7766 }
78- let ( first_block, to_add) = bbs. pick2_mut ( first, bb_idx) ;
79- let new_stmts = first_block. statements . iter ( ) . cloned ( ) . map ( |mut s| {
67+ // Take owenership of items now that we know we can optimize.
68+ let discr = discr. clone ( ) ;
69+
70+ bbs[ bb_idx] . terminator_mut ( ) . kind = TerminatorKind :: Goto { target : first } ;
71+ for s in bbs[ first] . statements . iter_mut ( ) {
8072 if let StatementKind :: Assign ( box ( _, ref mut rhs) ) = s. kind {
8173 let size = tcx. layout_of ( param_env. and ( switch_ty) ) . unwrap ( ) . size ;
8274 let const_cmp = Operand :: const_from_scalar (
@@ -86,17 +78,8 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
8678 rustc_span:: DUMMY_SP ,
8779 ) ;
8880 * rhs = Rvalue :: BinaryOp ( BinOp :: Eq , Operand :: Move ( discr) , const_cmp) ;
89- } else {
90- unreachable ! ( )
9181 }
92- s
93- } ) ;
94- to_add. statements . extend ( new_stmts) ;
95- to_add. terminator_mut ( ) . kind = first_dest;
96- did_remove_blocks = true ;
97- }
98- if did_remove_blocks {
99- simplify:: remove_dead_blocks ( body) ;
82+ }
10083 }
10184 }
10285}
0 commit comments