]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/transform/simplify_try.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_mir / transform / simplify_try.rs
1 //! The general point of the optimizations provided here is to simplify something like:
2 //!
3 //! ```rust
4 //! match x {
5 //! Ok(x) => Ok(x),
6 //! Err(x) => Err(x)
7 //! }
8 //! ```
9 //!
10 //! into just `x`.
11
12 use crate::transform::{MirPass, MirSource, simplify};
13 use rustc::ty::{TyCtxt, Ty};
14 use rustc::mir::*;
15 use rustc_target::abi::VariantIdx;
16 use itertools::Itertools as _;
17
18 /// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
19 ///
20 /// This is done by transforming basic blocks where the statements match:
21 ///
22 /// ```rust
23 /// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY );
24 /// ((_LOCAL_0 as Variant).FIELD: TY) = move _LOCAL_TMP;
25 /// discriminant(_LOCAL_0) = VAR_IDX;
26 /// ```
27 ///
28 /// into:
29 ///
30 /// ```rust
31 /// _LOCAL_0 = move _LOCAL_1
32 /// ```
33 pub struct SimplifyArmIdentity;
34
35 impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
36 fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
37 let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
38 for bb in basic_blocks {
39 // Need 3 statements:
40 let (s0, s1, s2) = match &mut *bb.statements {
41 [s0, s1, s2] => (s0, s1, s2),
42 _ => continue,
43 };
44
45 // Pattern match on the form we want:
46 let (local_tmp_s0, local_1, vf_s0) = match match_get_variant_field(s0) {
47 None => continue,
48 Some(x) => x,
49 };
50 let (local_tmp_s1, local_0, vf_s1) = match match_set_variant_field(s1) {
51 None => continue,
52 Some(x) => x,
53 };
54 if local_tmp_s0 != local_tmp_s1
55 // The field-and-variant information match up.
56 || vf_s0 != vf_s1
57 // Source and target locals have the same type.
58 // FIXME(Centril | oli-obk): possibly relax to same layout?
59 || local_decls[local_0].ty != local_decls[local_1].ty
60 // We're setting the discriminant of `local_0` to this variant.
61 || Some((local_0, vf_s0.var_idx)) != match_set_discr(s2)
62 {
63 continue;
64 }
65
66 // Right shape; transform!
67 match &mut s0.kind {
68 StatementKind::Assign(box (place, rvalue)) => {
69 *place = local_0.into();
70 *rvalue = Rvalue::Use(Operand::Move(local_1.into()));
71 }
72 _ => unreachable!(),
73 }
74 s1.make_nop();
75 s2.make_nop();
76 }
77 }
78 }
79
80 /// Match on:
81 /// ```rust
82 /// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
83 /// ```
84 fn match_get_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
85 match &stmt.kind {
86 StatementKind::Assign(box (place_into, rvalue_from)) => match rvalue_from {
87 Rvalue::Use(Operand::Copy(pf)) | Rvalue::Use(Operand::Move(pf)) => {
88 let local_into = place_into.as_local()?;
89 let (local_from, vf) = match_variant_field_place(&pf)?;
90 Some((local_into, local_from, vf))
91 }
92 _ => None,
93 },
94 _ => None,
95 }
96 }
97
98 /// Match on:
99 /// ```rust
100 /// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO;
101 /// ```
102 fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
103 match &stmt.kind {
104 StatementKind::Assign(box (place_from, rvalue_into)) => match rvalue_into {
105 Rvalue::Use(Operand::Move(place_into)) => {
106 let local_into = place_into.as_local()?;
107 let (local_from, vf) = match_variant_field_place(&place_from)?;
108 Some((local_into, local_from, vf))
109 }
110 _ => None,
111 },
112 _ => None,
113 }
114 }
115
116 /// Match on:
117 /// ```rust
118 /// discriminant(_LOCAL_TO_SET) = VAR_IDX;
119 /// ```
120 fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> {
121 match &stmt.kind {
122 StatementKind::SetDiscriminant { place, variant_index } => Some((
123 place.as_local()?,
124 *variant_index
125 )),
126 _ => None,
127 }
128 }
129
130 #[derive(PartialEq)]
131 struct VarField<'tcx> {
132 field: Field,
133 field_ty: Ty<'tcx>,
134 var_idx: VariantIdx,
135 }
136
137 /// Match on `((_LOCAL as Variant).FIELD: TY)`.
138 fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarField<'tcx>)> {
139 match place.as_ref() {
140 PlaceRef {
141 base: &PlaceBase::Local(local),
142 projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)],
143 } => Some((local, VarField { field, field_ty: ty, var_idx })),
144 _ => None,
145 }
146 }
147
148 /// Simplifies `SwitchInt(_) -> [targets]`,
149 /// where all the `targets` have the same form,
150 /// into `goto -> target_first`.
151 pub struct SimplifyBranchSame;
152
153 impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
154 fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
155 let mut did_remove_blocks = false;
156 let bbs = body.basic_blocks_mut();
157 for bb_idx in bbs.indices() {
158 let targets = match &bbs[bb_idx].terminator().kind {
159 TerminatorKind::SwitchInt { targets, .. } => targets,
160 _ => continue,
161 };
162
163 let mut iter_bbs_reachable = targets
164 .iter()
165 .map(|idx| (*idx, &bbs[*idx]))
166 .filter(|(_, bb)| {
167 // Reaching `unreachable` is UB so assume it doesn't happen.
168 bb.terminator().kind != TerminatorKind::Unreachable
169 // But `asm!(...)` could abort the program,
170 // so we cannot assume that the `unreachable` terminator itself is reachable.
171 // FIXME(Centril): use a normalization pass instead of a check.
172 || bb.statements.iter().any(|stmt| match stmt.kind {
173 StatementKind::InlineAsm(..) => true,
174 _ => false,
175 })
176 })
177 .peekable();
178
179 // We want to `goto -> bb_first`.
180 let bb_first = iter_bbs_reachable
181 .peek()
182 .map(|(idx, _)| *idx)
183 .unwrap_or(targets[0]);
184
185 // All successor basic blocks should have the exact same form.
186 let all_successors_equivalent = iter_bbs_reachable
187 .map(|(_, bb)| bb)
188 .tuple_windows()
189 .all(|(bb_l, bb_r)| {
190 bb_l.is_cleanup == bb_r.is_cleanup
191 && bb_l.terminator().kind == bb_r.terminator().kind
192 && bb_l.statements.iter().eq_by(&bb_r.statements, |x, y| x.kind == y.kind)
193 });
194
195 if all_successors_equivalent {
196 // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`.
197 bbs[bb_idx].terminator_mut().kind = TerminatorKind::Goto { target: bb_first };
198 did_remove_blocks = true;
199 }
200 }
201
202 if did_remove_blocks {
203 // We have dead blocks now, so remove those.
204 simplify::remove_dead_blocks(body);
205 }
206 }
207 }