1 use crate::transform
::MirPass
;
2 use rustc_middle
::mir
::*;
3 use rustc_middle
::ty
::TyCtxt
;
6 use super::simplify
::simplify_cfg
;
8 pub struct MatchBranchSimplification
;
10 /// If a source block is found that switches between two blocks that are exactly
11 /// the same modulo const bool assignments (e.g., one assigns true another false
12 /// to the same place), merge a target block statements into the source block,
13 /// using Eq / Ne comparison with switch value where const bools value differ.
19 /// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2];
37 /// _2 = Eq(move _3, const 42_isize);
42 impl<'tcx
> MirPass
<'tcx
> for MatchBranchSimplification
{
43 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, body
: &mut Body
<'tcx
>) {
44 if tcx
.sess
.mir_opt_level() < 3 {
48 let def_id
= body
.source
.def_id();
49 let param_env
= tcx
.param_env(def_id
);
51 let (bbs
, local_decls
) = body
.basic_blocks_and_local_decls_mut();
52 let mut should_cleanup
= false;
53 'outer
: for bb_idx
in bbs
.indices() {
54 if !tcx
.consider_optimizing(|| format
!("MatchBranchSimplification {:?} ", def_id
)) {
58 let (discr
, val
, switch_ty
, first
, second
) = match bbs
[bb_idx
].terminator().kind
{
59 TerminatorKind
::SwitchInt
{
60 discr
: ref discr @
(Operand
::Copy(_
) | Operand
::Move(_
)),
64 } if targets
.iter().len() == 1 => {
65 let (value
, target
) = targets
.iter().next().unwrap();
66 if target
== targets
.otherwise() {
69 (discr
, value
, switch_ty
, target
, targets
.otherwise())
71 // Only optimize switch int statements
75 // Check that destinations are identical, and if not, then don't optimize this block
76 if bbs
[first
].terminator().kind
!= bbs
[second
].terminator().kind
{
80 // Check that blocks are assignments of consts to the same place or same statement,
81 // and match up 1-1, if not don't optimize this block.
82 let first_stmts
= &bbs
[first
].statements
;
83 let scnd_stmts
= &bbs
[second
].statements
;
84 if first_stmts
.len() != scnd_stmts
.len() {
87 for (f
, s
) in iter
::zip(first_stmts
, scnd_stmts
) {
88 match (&f
.kind
, &s
.kind
) {
89 // If two statements are exactly the same, we can optimize.
90 (f_s
, s_s
) if f_s
== s_s
=> {}
92 // If two statements are const bool assignments to the same place, we can optimize.
94 StatementKind
::Assign(box (lhs_f
, Rvalue
::Use(Operand
::Constant(f_c
)))),
95 StatementKind
::Assign(box (lhs_s
, Rvalue
::Use(Operand
::Constant(s_c
)))),
97 && f_c
.literal
.ty().is_bool()
98 && s_c
.literal
.ty().is_bool()
99 && f_c
.literal
.try_eval_bool(tcx
, param_env
).is_some()
100 && s_c
.literal
.try_eval_bool(tcx
, param_env
).is_some() => {}
102 // Otherwise we cannot optimize. Try another block.
103 _
=> continue 'outer
,
106 // Take ownership of items now that we know we can optimize.
107 let discr
= discr
.clone();
109 // Introduce a temporary for the discriminant value.
110 let source_info
= bbs
[bb_idx
].terminator().source_info
;
111 let discr_local
= local_decls
.push(LocalDecl
::new(switch_ty
, source_info
.span
));
113 // We already checked that first and second are different blocks,
114 // and bb_idx has a different terminator from both of them.
115 let (from
, first
, second
) = bbs
.pick3_mut(bb_idx
, first
, second
);
117 let new_stmts
= iter
::zip(&first
.statements
, &second
.statements
).map(|(f
, s
)| {
118 match (&f
.kind
, &s
.kind
) {
119 (f_s
, s_s
) if f_s
== s_s
=> (*f
).clone(),
122 StatementKind
::Assign(box (lhs
, Rvalue
::Use(Operand
::Constant(f_c
)))),
123 StatementKind
::Assign(box (_
, Rvalue
::Use(Operand
::Constant(s_c
)))),
125 // From earlier loop we know that we are dealing with bool constants only:
126 let f_b
= f_c
.literal
.try_eval_bool(tcx
, param_env
).unwrap();
127 let s_b
= s_c
.literal
.try_eval_bool(tcx
, param_env
).unwrap();
129 // Same value in both blocks. Use statement as is.
132 // Different value between blocks. Make value conditional on switch condition.
133 let size
= tcx
.layout_of(param_env
.and(switch_ty
)).unwrap().size
;
134 let const_cmp
= Operand
::const_from_scalar(
137 crate::interpret
::Scalar
::from_uint(val
, size
),
138 rustc_span
::DUMMY_SP
,
140 let op
= if f_b { BinOp::Eq }
else { BinOp::Ne }
;
141 let rhs
= Rvalue
::BinaryOp(
143 box (Operand
::Copy(Place
::from(discr_local
)), const_cmp
),
146 source_info
: f
.source_info
,
147 kind
: StatementKind
::Assign(box (*lhs
, rhs
)),
157 .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) }
);
158 from
.statements
.push(Statement
{
160 kind
: StatementKind
::Assign(box (Place
::from(discr_local
), Rvalue
::Use(discr
))),
162 from
.statements
.extend(new_stmts
);
164 .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) }
);
165 from
.terminator_mut().kind
= first
.terminator().kind
.clone();
166 should_cleanup
= true;
170 simplify_cfg(tcx
, body
);