1
- use crate :: transform:: { MirPass , MirSource } ;
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} ;
2
13
use rustc:: ty:: { TyCtxt , Ty } ;
3
14
use rustc:: mir:: * ;
4
15
use rustc_target:: abi:: VariantIdx ;
16
+ use itertools:: Itertools as _;
5
17
6
- /// Simplifies arms of form `Variant(x) => x ` to just `x` .
18
+ /// Simplifies arms of form `Variant(x) => Variant(x) ` to just a move .
7
19
///
8
20
/// This is done by transforming basic blocks where the statements match:
9
21
///
@@ -21,10 +33,8 @@ use rustc_target::abi::VariantIdx;
21
33
pub struct SimplifyArmIdentity ;
22
34
23
35
impl < ' tcx > MirPass < ' tcx > for SimplifyArmIdentity {
24
- fn run_pass ( & self , _tcx : TyCtxt < ' tcx > , _src : MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
25
- for ( idx, bb) in body. basic_blocks_mut ( ) . iter_mut ( ) . enumerate ( ) {
26
- debug ! ( "SimplifyArmIdentity - bb{} = {:?}" , idx, bb) ;
27
-
36
+ fn run_pass ( & self , _: TyCtxt < ' tcx > , _: MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
37
+ for bb in body. basic_blocks_mut ( ) {
28
38
// Need 3 statements:
29
39
let ( s0, s1, s2) = match & mut * bb. statements {
30
40
[ s0, s1, s2] => ( s0, s1, s2) ,
@@ -46,9 +56,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
46
56
if local_tmp_s0 != local_tmp_s1 || vf_s0 != vf_s1 {
47
57
continue ;
48
58
}
49
- match match_set_discr ( s2) {
50
- Some ( ( local, var_idx) ) if local == local_0 && var_idx == vf_s0. var_idx => { }
51
- _ => continue ,
59
+ if Some ( ( local_0, vf_s0. var_idx ) ) != match_set_discr ( s2) {
60
+ continue ;
52
61
}
53
62
debug ! ( "SimplifyArmIdentity - set_discr" ) ;
54
63
@@ -60,8 +69,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
60
69
}
61
70
_ => unreachable ! ( ) ,
62
71
}
63
- s1. kind = StatementKind :: Nop ;
64
- s2. kind = StatementKind :: Nop ;
72
+ s1. make_nop ( ) ;
73
+ s2. make_nop ( ) ;
65
74
}
66
75
}
67
76
}
@@ -134,10 +143,138 @@ fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarFie
134
143
}
135
144
}
136
145
146
+ /// Simplifies `SwitchInt(_) -> [targets]`,
147
+ /// where all the `targets` have the same form,
148
+ /// into `goto -> target_first`.
137
149
pub struct SimplifyBranchSame ;
138
150
139
151
impl < ' tcx > MirPass < ' tcx > for SimplifyBranchSame {
140
- fn run_pass ( & self , _tcx : TyCtxt < ' tcx > , _src : MirSource < ' tcx > , _body : & mut Body < ' tcx > ) {
141
- //debug!("SimplifyBranchSame - simplifying {:?}", _body);
152
+ fn run_pass ( & self , _: TyCtxt < ' tcx > , _: MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
153
+ let bbs = body. basic_blocks_mut ( ) ;
154
+ for bb_idx in bbs. indices ( ) {
155
+ let targets = match & bbs[ bb_idx] . terminator ( ) . kind {
156
+ TerminatorKind :: SwitchInt { targets, .. } => targets,
157
+ _ => continue ,
158
+ } ;
159
+
160
+ // Reaching `unreachable` is UB so assume it doesn't happen.
161
+ let mut iter_bbs_reachable = targets
162
+ . iter ( )
163
+ . map ( |idx| ( * idx, & bbs[ * idx] ) )
164
+ . filter ( |( _, bb) | bb. terminator ( ) . kind != TerminatorKind :: Unreachable )
165
+ . peekable ( ) ;
166
+
167
+ // We want to `goto -> bb_first`.
168
+ let bb_first = iter_bbs_reachable
169
+ . peek ( )
170
+ . map ( |( idx, _) | * idx)
171
+ . unwrap_or ( targets[ 0 ] ) ;
172
+
173
+ // All successor basic blocks should have the exact same form.
174
+ let all_successors_equivalent = iter_bbs_reachable
175
+ . map ( |( _, bb) | bb)
176
+ . tuple_windows ( )
177
+ . all ( |( bb_l, bb_r) | {
178
+ bb_l. is_cleanup == bb_r. is_cleanup
179
+ && bb_l. terminator ( ) . kind == bb_r. terminator ( ) . kind
180
+ && bb_l. statements . iter ( ) . eq_by ( & bb_r. statements , |x, y| x. kind == y. kind )
181
+ } ) ;
182
+
183
+ if all_successors_equivalent {
184
+ // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`.
185
+ bbs[ bb_idx] . terminator_mut ( ) . kind = TerminatorKind :: Goto { target : bb_first } ;
186
+ }
187
+ }
188
+
189
+ // We may have dead blocks now, so remvoe those.
190
+ simplify:: remove_dead_blocks ( body) ;
191
+ }
192
+ }
193
+
194
+ /*
195
+
196
+ KEEPSAKE: TODO REMOVE IF NOT NECESSARY!
197
+
198
+ fn statement_semantic_eq(sa: &StatementKind<'_>, sb: &StatementKind<'_>) -> bool {
199
+ use StatementKind::*;
200
+ match (sb, sa) {
201
+ (AscribeUserType(pa, va), AscribeUserType(pb, vb)) => pa == pb && va == vb,
202
+ (Assign(a), Assign(b)) => a == b,
203
+ (FakeRead(fa, pa), FakeRead(fb, pb)) => fa == fb && pa == pb,
204
+ (InlineAsm(a), InlineAsm(b)) => a == b,
205
+ (Nop, StatementKind::Nop) => true,
206
+ (Retag(ra, pa), Retag(rb, pb)) => ra == rb && pa == pb,
207
+ (
208
+ SetDiscriminant { place: pa, variant_index: va },
209
+ SetDiscriminant { place: pb, variant_index: vb },
210
+ ) => pa == pb && va == vb,
211
+ (StorageDead(a), StorageDead(b)) => a == b,
212
+ (StorageLive(a), StorageLive(b)) => a == b,
213
+ (AscribeUserType(..), _) | (_, AscribeUserType(..))
214
+ | (StorageDead(..), _) | (_, StorageDead(..))
215
+ | (Assign(..), _) | (_, Assign(..))
216
+ | (FakeRead(..), _) | (_, FakeRead(..))
217
+ | (InlineAsm(..), _) | (_, InlineAsm(..))
218
+ | (Nop, _) | (_, Nop)
219
+ | (Retag(..), _) | (_, Retag(..))
220
+ | (SetDiscriminant { .. }, _) | (_, SetDiscriminant { .. }) => true,
221
+ }
222
+ }
223
+
224
+ fn terminator_semantic_eq(ta: &TerminatorKind<'_>, tb: &TerminatorKind<'_>) -> bool {
225
+ use TerminatorKind::*;
226
+ match (ta, tb) {
227
+ (Goto { target: a }, Goto { target: b }) => a == b,
228
+ (Resume, Resume)
229
+ | (Abort, Abort)
230
+ | (Return, Return)
231
+ | (Unreachable, Unreachable)
232
+ | (GeneratorDrop, GeneratorDrop) => true,
233
+ (
234
+ SwitchInt { discr: da, switch_ty: sa, targets: ta, values: _ },
235
+ SwitchInt { discr: db, switch_ty: sb, targets: tb, values: _ },
236
+ ) => da == db && sa == sb && ta == tb,
237
+ (
238
+ Drop { location: la, target: ta, unwind: ua },
239
+ Drop { location: lb, target: tb, unwind: ub },
240
+ ) => la == lb && ta == tb && ua == ub,
241
+ (
242
+ DropAndReplace { location: la, target: ta, unwind: ua, value: va },
243
+ DropAndReplace { location: lb, target: tb, unwind: ub, value: vb },
244
+ ) => la == lb && ta == tb && ua == ub && va == vb,
245
+ (
246
+ Call { func: fa, args: aa, destination: da, cleanup: ca, from_hir_call: _ },
247
+ Call { func: fb, args: ab, destination: db, cleanup: cb, from_hir_call: _ },
248
+ ) => fa == fb && aa == ab && da == db && ca == cb,
249
+ (
250
+ Assert { cond: coa, expected: ea, msg: ma, target: ta, cleanup: cla },
251
+ Assert { cond: cob, expected: eb, msg: mb, target: tb, cleanup: clb },
252
+ ) => coa == cob && ea == eb && ma == mb && ta == tb && cla == clb,
253
+ (
254
+ Yield { value: va, resume: ra, drop: da },
255
+ Yield { value: vb, resume: rb, drop: db },
256
+ ) => va == vb && ra == rb && da == db,
257
+ (
258
+ FalseEdges { real_target: ra, imaginary_target: ia },
259
+ FalseEdges { real_target: rb, imaginary_target: ib },
260
+ ) => ra == rb && ia == ib,
261
+ (
262
+ FalseUnwind { real_target: ra, unwind: ua },
263
+ FalseUnwind { real_target: rb, unwind: ub },
264
+ ) => ra == rb && ua == ub,
265
+ (Goto { .. }, _) | (_, Goto { .. })
266
+ | (Resume, _) | (_, Resume)
267
+ | (Abort, _) | (_, Abort)
268
+ | (Return, _) | (_, Return)
269
+ | (Unreachable, _) | (_, Unreachable)
270
+ | (GeneratorDrop, _) | (_, GeneratorDrop)
271
+ | (SwitchInt { .. }, _) | (_, SwitchInt { .. })
272
+ | (Drop { .. }, _) | (_, Drop { .. })
273
+ | (DropAndReplace { .. }, _) | (_, DropAndReplace { .. })
274
+ | (Call { .. }, _) | (_, Call { .. })
275
+ | (Assert { .. }, _) | (_, Assert { .. })
276
+ | (Yield { .. }, _) | (_, Yield { .. })
277
+ | (FalseEdges { .. }, _) | (_, FalseEdges { .. }) => false,
142
278
}
143
279
}
280
+ */
0 commit comments