]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/dataflow/framework/direction.rs
Refactor how SwitchInt stores jump targets
[rust.git] / compiler / rustc_mir / src / dataflow / framework / direction.rs
1 use rustc_index::bit_set::BitSet;
2 use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets};
3 use rustc_middle::ty::TyCtxt;
4 use std::ops::RangeInclusive;
5
6 use super::visitor::{ResultsVisitable, ResultsVisitor};
7 use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
8
9 pub trait Direction {
10     fn is_forward() -> bool;
11
12     fn is_backward() -> bool {
13         !Self::is_forward()
14     }
15
16     /// Applies all effects between the given `EffectIndex`s.
17     ///
18     /// `effects.start()` must precede or equal `effects.end()` in this direction.
19     fn apply_effects_in_range<A>(
20         analysis: &A,
21         state: &mut A::Domain,
22         block: BasicBlock,
23         block_data: &mir::BasicBlockData<'tcx>,
24         effects: RangeInclusive<EffectIndex>,
25     ) where
26         A: Analysis<'tcx>;
27
28     fn apply_effects_in_block<A>(
29         analysis: &A,
30         state: &mut A::Domain,
31         block: BasicBlock,
32         block_data: &mir::BasicBlockData<'tcx>,
33     ) where
34         A: Analysis<'tcx>;
35
36     fn gen_kill_effects_in_block<A>(
37         analysis: &A,
38         trans: &mut GenKillSet<A::Idx>,
39         block: BasicBlock,
40         block_data: &mir::BasicBlockData<'tcx>,
41     ) where
42         A: GenKillAnalysis<'tcx>;
43
44     fn visit_results_in_block<F, R>(
45         state: &mut F,
46         block: BasicBlock,
47         block_data: &'mir mir::BasicBlockData<'tcx>,
48         results: &R,
49         vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
50     ) where
51         R: ResultsVisitable<'tcx, FlowState = F>;
52
53     fn join_state_into_successors_of<A>(
54         analysis: &A,
55         tcx: TyCtxt<'tcx>,
56         body: &mir::Body<'tcx>,
57         dead_unwinds: Option<&BitSet<BasicBlock>>,
58         exit_state: &mut A::Domain,
59         block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
60         propagate: impl FnMut(BasicBlock, &A::Domain),
61     ) where
62         A: Analysis<'tcx>;
63 }
64
65 /// Dataflow that runs from the exit of a block (the terminator), to its entry (the first statement).
66 pub struct Backward;
67
68 impl Direction for Backward {
69     fn is_forward() -> bool {
70         false
71     }
72
73     fn apply_effects_in_block<A>(
74         analysis: &A,
75         state: &mut A::Domain,
76         block: BasicBlock,
77         block_data: &mir::BasicBlockData<'tcx>,
78     ) where
79         A: Analysis<'tcx>,
80     {
81         let terminator = block_data.terminator();
82         let location = Location { block, statement_index: block_data.statements.len() };
83         analysis.apply_before_terminator_effect(state, terminator, location);
84         analysis.apply_terminator_effect(state, terminator, location);
85
86         for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
87             let location = Location { block, statement_index };
88             analysis.apply_before_statement_effect(state, statement, location);
89             analysis.apply_statement_effect(state, statement, location);
90         }
91     }
92
93     fn gen_kill_effects_in_block<A>(
94         analysis: &A,
95         trans: &mut GenKillSet<A::Idx>,
96         block: BasicBlock,
97         block_data: &mir::BasicBlockData<'tcx>,
98     ) where
99         A: GenKillAnalysis<'tcx>,
100     {
101         let terminator = block_data.terminator();
102         let location = Location { block, statement_index: block_data.statements.len() };
103         analysis.before_terminator_effect(trans, terminator, location);
104         analysis.terminator_effect(trans, terminator, location);
105
106         for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
107             let location = Location { block, statement_index };
108             analysis.before_statement_effect(trans, statement, location);
109             analysis.statement_effect(trans, statement, location);
110         }
111     }
112
113     fn apply_effects_in_range<A>(
114         analysis: &A,
115         state: &mut A::Domain,
116         block: BasicBlock,
117         block_data: &mir::BasicBlockData<'tcx>,
118         effects: RangeInclusive<EffectIndex>,
119     ) where
120         A: Analysis<'tcx>,
121     {
122         let (from, to) = (*effects.start(), *effects.end());
123         let terminator_index = block_data.statements.len();
124
125         assert!(from.statement_index <= terminator_index);
126         assert!(!to.precedes_in_backward_order(from));
127
128         // Handle the statement (or terminator) at `from`.
129
130         let next_effect = match from.effect {
131             // If we need to apply the terminator effect in all or in part, do so now.
132             _ if from.statement_index == terminator_index => {
133                 let location = Location { block, statement_index: from.statement_index };
134                 let terminator = block_data.terminator();
135
136                 if from.effect == Effect::Before {
137                     analysis.apply_before_terminator_effect(state, terminator, location);
138                     if to == Effect::Before.at_index(terminator_index) {
139                         return;
140                     }
141                 }
142
143                 analysis.apply_terminator_effect(state, terminator, location);
144                 if to == Effect::Primary.at_index(terminator_index) {
145                     return;
146                 }
147
148                 // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons
149                 // with `to`.
150                 from.statement_index - 1
151             }
152
153             Effect::Primary => {
154                 let location = Location { block, statement_index: from.statement_index };
155                 let statement = &block_data.statements[from.statement_index];
156
157                 analysis.apply_statement_effect(state, statement, location);
158                 if to == Effect::Primary.at_index(from.statement_index) {
159                     return;
160                 }
161
162                 from.statement_index - 1
163             }
164
165             Effect::Before => from.statement_index,
166         };
167
168         // Handle all statements between `first_unapplied_idx` and `to.statement_index`.
169
170         for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
171             let location = Location { block, statement_index };
172             let statement = &block_data.statements[statement_index];
173             analysis.apply_before_statement_effect(state, statement, location);
174             analysis.apply_statement_effect(state, statement, location);
175         }
176
177         // Handle the statement at `to`.
178
179         let location = Location { block, statement_index: to.statement_index };
180         let statement = &block_data.statements[to.statement_index];
181         analysis.apply_before_statement_effect(state, statement, location);
182
183         if to.effect == Effect::Before {
184             return;
185         }
186
187         analysis.apply_statement_effect(state, statement, location);
188     }
189
190     fn visit_results_in_block<F, R>(
191         state: &mut F,
192         block: BasicBlock,
193         block_data: &'mir mir::BasicBlockData<'tcx>,
194         results: &R,
195         vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
196     ) where
197         R: ResultsVisitable<'tcx, FlowState = F>,
198     {
199         results.reset_to_block_entry(state, block);
200
201         vis.visit_block_end(&state, block_data, block);
202
203         // Terminator
204         let loc = Location { block, statement_index: block_data.statements.len() };
205         let term = block_data.terminator();
206         results.reconstruct_before_terminator_effect(state, term, loc);
207         vis.visit_terminator_before_primary_effect(state, term, loc);
208         results.reconstruct_terminator_effect(state, term, loc);
209         vis.visit_terminator_after_primary_effect(state, term, loc);
210
211         for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
212             let loc = Location { block, statement_index };
213             results.reconstruct_before_statement_effect(state, stmt, loc);
214             vis.visit_statement_before_primary_effect(state, stmt, loc);
215             results.reconstruct_statement_effect(state, stmt, loc);
216             vis.visit_statement_after_primary_effect(state, stmt, loc);
217         }
218
219         vis.visit_block_start(state, block_data, block);
220     }
221
222     fn join_state_into_successors_of<A>(
223         analysis: &A,
224         _tcx: TyCtxt<'tcx>,
225         body: &mir::Body<'tcx>,
226         dead_unwinds: Option<&BitSet<BasicBlock>>,
227         exit_state: &mut A::Domain,
228         (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
229         mut propagate: impl FnMut(BasicBlock, &A::Domain),
230     ) where
231         A: Analysis<'tcx>,
232     {
233         for pred in body.predecessors()[bb].iter().copied() {
234             match body[pred].terminator().kind {
235                 // Apply terminator-specific edge effects.
236                 //
237                 // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
238                 mir::TerminatorKind::Call {
239                     destination: Some((return_place, dest)),
240                     ref func,
241                     ref args,
242                     ..
243                 } if dest == bb => {
244                     let mut tmp = exit_state.clone();
245                     analysis.apply_call_return_effect(&mut tmp, pred, func, args, return_place);
246                     propagate(pred, &tmp);
247                 }
248
249                 mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
250                     let mut tmp = exit_state.clone();
251                     analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg);
252                     propagate(pred, &tmp);
253                 }
254
255                 // Ignore dead unwinds.
256                 mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
257                 | mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
258                 | mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
259                 | mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
260                 | mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
261                     if unwind == bb =>
262                 {
263                     if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
264                         propagate(pred, exit_state);
265                     }
266                 }
267
268                 _ => propagate(pred, exit_state),
269             }
270         }
271     }
272 }
273
274 /// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
275 pub struct Forward;
276
277 impl Direction for Forward {
278     fn is_forward() -> bool {
279         true
280     }
281
282     fn apply_effects_in_block<A>(
283         analysis: &A,
284         state: &mut A::Domain,
285         block: BasicBlock,
286         block_data: &mir::BasicBlockData<'tcx>,
287     ) where
288         A: Analysis<'tcx>,
289     {
290         for (statement_index, statement) in block_data.statements.iter().enumerate() {
291             let location = Location { block, statement_index };
292             analysis.apply_before_statement_effect(state, statement, location);
293             analysis.apply_statement_effect(state, statement, location);
294         }
295
296         let terminator = block_data.terminator();
297         let location = Location { block, statement_index: block_data.statements.len() };
298         analysis.apply_before_terminator_effect(state, terminator, location);
299         analysis.apply_terminator_effect(state, terminator, location);
300     }
301
302     fn gen_kill_effects_in_block<A>(
303         analysis: &A,
304         trans: &mut GenKillSet<A::Idx>,
305         block: BasicBlock,
306         block_data: &mir::BasicBlockData<'tcx>,
307     ) where
308         A: GenKillAnalysis<'tcx>,
309     {
310         for (statement_index, statement) in block_data.statements.iter().enumerate() {
311             let location = Location { block, statement_index };
312             analysis.before_statement_effect(trans, statement, location);
313             analysis.statement_effect(trans, statement, location);
314         }
315
316         let terminator = block_data.terminator();
317         let location = Location { block, statement_index: block_data.statements.len() };
318         analysis.before_terminator_effect(trans, terminator, location);
319         analysis.terminator_effect(trans, terminator, location);
320     }
321
322     fn apply_effects_in_range<A>(
323         analysis: &A,
324         state: &mut A::Domain,
325         block: BasicBlock,
326         block_data: &mir::BasicBlockData<'tcx>,
327         effects: RangeInclusive<EffectIndex>,
328     ) where
329         A: Analysis<'tcx>,
330     {
331         let (from, to) = (*effects.start(), *effects.end());
332         let terminator_index = block_data.statements.len();
333
334         assert!(to.statement_index <= terminator_index);
335         assert!(!to.precedes_in_forward_order(from));
336
337         // If we have applied the before affect of the statement or terminator at `from` but not its
338         // after effect, do so now and start the loop below from the next statement.
339
340         let first_unapplied_index = match from.effect {
341             Effect::Before => from.statement_index,
342
343             Effect::Primary if from.statement_index == terminator_index => {
344                 debug_assert_eq!(from, to);
345
346                 let location = Location { block, statement_index: terminator_index };
347                 let terminator = block_data.terminator();
348                 analysis.apply_terminator_effect(state, terminator, location);
349                 return;
350             }
351
352             Effect::Primary => {
353                 let location = Location { block, statement_index: from.statement_index };
354                 let statement = &block_data.statements[from.statement_index];
355                 analysis.apply_statement_effect(state, statement, location);
356
357                 // If we only needed to apply the after effect of the statement at `idx`, we are done.
358                 if from == to {
359                     return;
360                 }
361
362                 from.statement_index + 1
363             }
364         };
365
366         // Handle all statements between `from` and `to` whose effects must be applied in full.
367
368         for statement_index in first_unapplied_index..to.statement_index {
369             let location = Location { block, statement_index };
370             let statement = &block_data.statements[statement_index];
371             analysis.apply_before_statement_effect(state, statement, location);
372             analysis.apply_statement_effect(state, statement, location);
373         }
374
375         // Handle the statement or terminator at `to`.
376
377         let location = Location { block, statement_index: to.statement_index };
378         if to.statement_index == terminator_index {
379             let terminator = block_data.terminator();
380             analysis.apply_before_terminator_effect(state, terminator, location);
381
382             if to.effect == Effect::Primary {
383                 analysis.apply_terminator_effect(state, terminator, location);
384             }
385         } else {
386             let statement = &block_data.statements[to.statement_index];
387             analysis.apply_before_statement_effect(state, statement, location);
388
389             if to.effect == Effect::Primary {
390                 analysis.apply_statement_effect(state, statement, location);
391             }
392         }
393     }
394
395     fn visit_results_in_block<F, R>(
396         state: &mut F,
397         block: BasicBlock,
398         block_data: &'mir mir::BasicBlockData<'tcx>,
399         results: &R,
400         vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
401     ) where
402         R: ResultsVisitable<'tcx, FlowState = F>,
403     {
404         results.reset_to_block_entry(state, block);
405
406         vis.visit_block_start(state, block_data, block);
407
408         for (statement_index, stmt) in block_data.statements.iter().enumerate() {
409             let loc = Location { block, statement_index };
410             results.reconstruct_before_statement_effect(state, stmt, loc);
411             vis.visit_statement_before_primary_effect(state, stmt, loc);
412             results.reconstruct_statement_effect(state, stmt, loc);
413             vis.visit_statement_after_primary_effect(state, stmt, loc);
414         }
415
416         let loc = Location { block, statement_index: block_data.statements.len() };
417         let term = block_data.terminator();
418         results.reconstruct_before_terminator_effect(state, term, loc);
419         vis.visit_terminator_before_primary_effect(state, term, loc);
420         results.reconstruct_terminator_effect(state, term, loc);
421         vis.visit_terminator_after_primary_effect(state, term, loc);
422
423         vis.visit_block_end(state, block_data, block);
424     }
425
426     fn join_state_into_successors_of<A>(
427         analysis: &A,
428         _tcx: TyCtxt<'tcx>,
429         _body: &mir::Body<'tcx>,
430         dead_unwinds: Option<&BitSet<BasicBlock>>,
431         exit_state: &mut A::Domain,
432         (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
433         mut propagate: impl FnMut(BasicBlock, &A::Domain),
434     ) where
435         A: Analysis<'tcx>,
436     {
437         use mir::TerminatorKind::*;
438         match bb_data.terminator().kind {
439             Return | Resume | Abort | GeneratorDrop | Unreachable => {}
440
441             Goto { target } => propagate(target, exit_state),
442
443             Assert { target, cleanup: unwind, expected: _, msg: _, cond: _ }
444             | Drop { target, unwind, place: _ }
445             | DropAndReplace { target, unwind, value: _, place: _ }
446             | FalseUnwind { real_target: target, unwind } => {
447                 if let Some(unwind) = unwind {
448                     if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
449                         propagate(unwind, exit_state);
450                     }
451                 }
452
453                 propagate(target, exit_state);
454             }
455
456             FalseEdge { real_target, imaginary_target } => {
457                 propagate(real_target, exit_state);
458                 propagate(imaginary_target, exit_state);
459             }
460
461             Yield { resume: target, drop, resume_arg, value: _ } => {
462                 if let Some(drop) = drop {
463                     propagate(drop, exit_state);
464                 }
465
466                 analysis.apply_yield_resume_effect(exit_state, target, resume_arg);
467                 propagate(target, exit_state);
468             }
469
470             Call { cleanup, destination, ref func, ref args, from_hir_call: _, fn_span: _ } => {
471                 if let Some(unwind) = cleanup {
472                     if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
473                         propagate(unwind, exit_state);
474                     }
475                 }
476
477                 if let Some((dest_place, target)) = destination {
478                     // N.B.: This must be done *last*, otherwise the unwind path will see the call
479                     // return effect.
480                     analysis.apply_call_return_effect(exit_state, bb, func, args, dest_place);
481                     propagate(target, exit_state);
482                 }
483             }
484
485             InlineAsm { template: _, operands: _, options: _, line_spans: _, destination } => {
486                 if let Some(target) = destination {
487                     propagate(target, exit_state);
488                 }
489             }
490
491             SwitchInt { ref targets, ref discr, switch_ty: _ } => {
492                 let mut applier = SwitchIntEdgeEffectApplier {
493                     exit_state,
494                     targets,
495                     propagate,
496                     effects_applied: false,
497                 };
498
499                 analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
500
501                 let SwitchIntEdgeEffectApplier {
502                     exit_state, mut propagate, effects_applied, ..
503                 } = applier;
504
505                 if !effects_applied {
506                     for target in targets.all_targets() {
507                         propagate(*target, exit_state);
508                     }
509                 }
510             }
511         }
512     }
513 }
514
515 struct SwitchIntEdgeEffectApplier<'a, D, F> {
516     exit_state: &'a mut D,
517     targets: &'a SwitchTargets<'a>,
518     propagate: F,
519
520     effects_applied: bool,
521 }
522
523 impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F>
524 where
525     D: Clone,
526     F: FnMut(BasicBlock, &D),
527 {
528     fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
529         assert!(!self.effects_applied);
530
531         let mut tmp = None;
532         for (value, target) in self.targets.iter() {
533             let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
534             apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
535             (self.propagate)(target, tmp);
536         }
537
538         // Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
539         // so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
540         let otherwise = self.targets.otherwise();
541         apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
542         (self.propagate)(otherwise, self.exit_state);
543
544         self.effects_applied = true;
545     }
546 }
547
548 /// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
549 /// the more efficient `clone_from` if `opt` was `Some`.
550 ///
551 /// Returns a mutable reference to the new clone that resides in `opt`.
552 //
553 // FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
554 // standard library?
555 fn opt_clone_from_or_clone<T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
556     if opt.is_some() {
557         let ret = opt.as_mut().unwrap();
558         ret.clone_from(val);
559         ret
560     } else {
561         *opt = Some(val.clone());
562         opt.as_mut().unwrap()
563     }
564 }