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} ;
213use rustc:: ty:: { TyCtxt , Ty } ;
314use rustc:: mir:: * ;
415use rustc_target:: abi:: VariantIdx ;
16+ use itertools:: Itertools as _;
517
6- /// Simplifies arms of form `Variant(x) => x ` to just `x` .
18+ /// Simplifies arms of form `Variant(x) => Variant(x) ` to just a move .
719///
820/// This is done by transforming basic blocks where the statements match:
921///
@@ -21,10 +33,8 @@ use rustc_target::abi::VariantIdx;
2133pub struct SimplifyArmIdentity ;
2234
2335impl < ' 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 ( ) {
2838 // Need 3 statements:
2939 let ( s0, s1, s2) = match & mut * bb. statements {
3040 [ s0, s1, s2] => ( s0, s1, s2) ,
@@ -46,9 +56,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
4656 if local_tmp_s0 != local_tmp_s1 || vf_s0 != vf_s1 {
4757 continue ;
4858 }
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 ;
5261 }
5362 debug ! ( "SimplifyArmIdentity - set_discr" ) ;
5463
@@ -60,8 +69,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
6069 }
6170 _ => unreachable ! ( ) ,
6271 }
63- s1. kind = StatementKind :: Nop ;
64- s2. kind = StatementKind :: Nop ;
72+ s1. make_nop ( ) ;
73+ s2. make_nop ( ) ;
6574 }
6675 }
6776}
@@ -134,10 +143,138 @@ fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarFie
134143 }
135144}
136145
146+ /// Simplifies `SwitchInt(_) -> [targets]`,
147+ /// where all the `targets` have the same form,
148+ /// into `goto -> target_first`.
137149pub struct SimplifyBranchSame ;
138150
139151impl < ' 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,
142278 }
143279}
280+ */
0 commit comments