rustc_mir_dataflow/framework/
direction.rs

1use std::ops::RangeInclusive;
2
3use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
4
5use super::visitor::ResultsVisitor;
6use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
7
8pub trait Direction {
9    const IS_FORWARD: bool;
10    const IS_BACKWARD: bool = !Self::IS_FORWARD;
11
12    /// Called by `iterate_to_fixpoint` during initial analysis computation.
13    fn apply_effects_in_block<'mir, 'tcx, A>(
14        analysis: &mut A,
15        body: &mir::Body<'tcx>,
16        state: &mut A::Domain,
17        block: BasicBlock,
18        block_data: &'mir mir::BasicBlockData<'tcx>,
19        propagate: impl FnMut(BasicBlock, &A::Domain),
20    ) where
21        A: Analysis<'tcx>;
22
23    /// Called by `ResultsCursor` to recompute the domain value for a location
24    /// in a basic block. Applies all effects between the given `EffectIndex`s.
25    ///
26    /// `effects.start()` must precede or equal `effects.end()` in this direction.
27    fn apply_effects_in_range<'tcx, A>(
28        analysis: &mut A,
29        state: &mut A::Domain,
30        block: BasicBlock,
31        block_data: &mir::BasicBlockData<'tcx>,
32        effects: RangeInclusive<EffectIndex>,
33    ) where
34        A: Analysis<'tcx>;
35
36    /// Called by `ResultsVisitor` to recompute the analysis domain values for
37    /// all locations in a basic block (starting from the entry value stored
38    /// in `Results`) and to visit them with `vis`.
39    fn visit_results_in_block<'mir, 'tcx, A>(
40        state: &mut A::Domain,
41        block: BasicBlock,
42        block_data: &'mir mir::BasicBlockData<'tcx>,
43        results: &mut Results<'tcx, A>,
44        vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
45    ) where
46        A: Analysis<'tcx>;
47}
48
49/// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement).
50pub struct Backward;
51
52impl Direction for Backward {
53    const IS_FORWARD: bool = false;
54
55    fn apply_effects_in_block<'mir, 'tcx, A>(
56        analysis: &mut A,
57        body: &mir::Body<'tcx>,
58        state: &mut A::Domain,
59        block: BasicBlock,
60        block_data: &'mir mir::BasicBlockData<'tcx>,
61        mut propagate: impl FnMut(BasicBlock, &A::Domain),
62    ) where
63        A: Analysis<'tcx>,
64    {
65        let terminator = block_data.terminator();
66        let location = Location { block, statement_index: block_data.statements.len() };
67        analysis.apply_early_terminator_effect(state, terminator, location);
68        analysis.apply_primary_terminator_effect(state, terminator, location);
69        for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
70            let location = Location { block, statement_index };
71            analysis.apply_early_statement_effect(state, statement, location);
72            analysis.apply_primary_statement_effect(state, statement, location);
73        }
74
75        let exit_state = state;
76        for pred in body.basic_blocks.predecessors()[block].iter().copied() {
77            match body[pred].terminator().kind {
78                // Apply terminator-specific edge effects.
79                mir::TerminatorKind::Call { destination, target: Some(dest), .. }
80                    if dest == block =>
81                {
82                    let mut tmp = exit_state.clone();
83                    analysis.apply_call_return_effect(
84                        &mut tmp,
85                        pred,
86                        CallReturnPlaces::Call(destination),
87                    );
88                    propagate(pred, &tmp);
89                }
90
91                mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
92                    if targets.contains(&block) =>
93                {
94                    let mut tmp = exit_state.clone();
95                    analysis.apply_call_return_effect(
96                        &mut tmp,
97                        pred,
98                        CallReturnPlaces::InlineAsm(operands),
99                    );
100                    propagate(pred, &tmp);
101                }
102
103                mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => {
104                    let mut tmp = exit_state.clone();
105                    analysis.apply_call_return_effect(
106                        &mut tmp,
107                        resume,
108                        CallReturnPlaces::Yield(resume_arg),
109                    );
110                    propagate(pred, &tmp);
111                }
112
113                mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
114                    if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
115                        let values = &body.basic_blocks.switch_sources()[&(block, pred)];
116                        let targets =
117                            values.iter().map(|&value| SwitchIntTarget { value, target: block });
118
119                        let mut tmp = analysis.bottom_value(body);
120                        for target in targets {
121                            tmp.clone_from(&exit_state);
122                            analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
123                            propagate(pred, &tmp);
124                        }
125                    } else {
126                        propagate(pred, exit_state)
127                    }
128                }
129
130                _ => propagate(pred, exit_state),
131            }
132        }
133    }
134
135    fn apply_effects_in_range<'tcx, A>(
136        analysis: &mut A,
137        state: &mut A::Domain,
138        block: BasicBlock,
139        block_data: &mir::BasicBlockData<'tcx>,
140        effects: RangeInclusive<EffectIndex>,
141    ) where
142        A: Analysis<'tcx>,
143    {
144        let (from, to) = (*effects.start(), *effects.end());
145        let terminator_index = block_data.statements.len();
146
147        assert!(from.statement_index <= terminator_index);
148        assert!(!to.precedes_in_backward_order(from));
149
150        // Handle the statement (or terminator) at `from`.
151
152        let next_effect = match from.effect {
153            // If we need to apply the terminator effect in all or in part, do so now.
154            _ if from.statement_index == terminator_index => {
155                let location = Location { block, statement_index: from.statement_index };
156                let terminator = block_data.terminator();
157
158                if from.effect == Effect::Early {
159                    analysis.apply_early_terminator_effect(state, terminator, location);
160                    if to == Effect::Early.at_index(terminator_index) {
161                        return;
162                    }
163                }
164
165                analysis.apply_primary_terminator_effect(state, terminator, location);
166                if to == Effect::Primary.at_index(terminator_index) {
167                    return;
168                }
169
170                // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons
171                // with `to`.
172                from.statement_index - 1
173            }
174
175            Effect::Primary => {
176                let location = Location { block, statement_index: from.statement_index };
177                let statement = &block_data.statements[from.statement_index];
178
179                analysis.apply_primary_statement_effect(state, statement, location);
180                if to == Effect::Primary.at_index(from.statement_index) {
181                    return;
182                }
183
184                from.statement_index - 1
185            }
186
187            Effect::Early => from.statement_index,
188        };
189
190        // Handle all statements between `first_unapplied_idx` and `to.statement_index`.
191
192        for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
193            let location = Location { block, statement_index };
194            let statement = &block_data.statements[statement_index];
195            analysis.apply_early_statement_effect(state, statement, location);
196            analysis.apply_primary_statement_effect(state, statement, location);
197        }
198
199        // Handle the statement at `to`.
200
201        let location = Location { block, statement_index: to.statement_index };
202        let statement = &block_data.statements[to.statement_index];
203        analysis.apply_early_statement_effect(state, statement, location);
204
205        if to.effect == Effect::Early {
206            return;
207        }
208
209        analysis.apply_primary_statement_effect(state, statement, location);
210    }
211
212    fn visit_results_in_block<'mir, 'tcx, A>(
213        state: &mut A::Domain,
214        block: BasicBlock,
215        block_data: &'mir mir::BasicBlockData<'tcx>,
216        results: &mut Results<'tcx, A>,
217        vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
218    ) where
219        A: Analysis<'tcx>,
220    {
221        state.clone_from(results.entry_set_for_block(block));
222
223        vis.visit_block_end(state);
224
225        let loc = Location { block, statement_index: block_data.statements.len() };
226        let term = block_data.terminator();
227        results.analysis.apply_early_terminator_effect(state, term, loc);
228        vis.visit_after_early_terminator_effect(results, state, term, loc);
229        results.analysis.apply_primary_terminator_effect(state, term, loc);
230        vis.visit_after_primary_terminator_effect(results, state, term, loc);
231
232        for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
233            let loc = Location { block, statement_index };
234            results.analysis.apply_early_statement_effect(state, stmt, loc);
235            vis.visit_after_early_statement_effect(results, state, stmt, loc);
236            results.analysis.apply_primary_statement_effect(state, stmt, loc);
237            vis.visit_after_primary_statement_effect(results, state, stmt, loc);
238        }
239
240        vis.visit_block_start(state);
241    }
242}
243
244/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
245pub struct Forward;
246
247impl Direction for Forward {
248    const IS_FORWARD: bool = true;
249
250    fn apply_effects_in_block<'mir, 'tcx, A>(
251        analysis: &mut A,
252        body: &mir::Body<'tcx>,
253        state: &mut A::Domain,
254        block: BasicBlock,
255        block_data: &'mir mir::BasicBlockData<'tcx>,
256        mut propagate: impl FnMut(BasicBlock, &A::Domain),
257    ) where
258        A: Analysis<'tcx>,
259    {
260        for (statement_index, statement) in block_data.statements.iter().enumerate() {
261            let location = Location { block, statement_index };
262            analysis.apply_early_statement_effect(state, statement, location);
263            analysis.apply_primary_statement_effect(state, statement, location);
264        }
265        let terminator = block_data.terminator();
266        let location = Location { block, statement_index: block_data.statements.len() };
267        analysis.apply_early_terminator_effect(state, terminator, location);
268        let edges = analysis.apply_primary_terminator_effect(state, terminator, location);
269
270        let exit_state = state;
271        match edges {
272            TerminatorEdges::None => {}
273            TerminatorEdges::Single(target) => propagate(target, exit_state),
274            TerminatorEdges::Double(target, unwind) => {
275                propagate(target, exit_state);
276                propagate(unwind, exit_state);
277            }
278            TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
279                // This must be done *first*, otherwise the unwind path will see the assignments.
280                if let Some(cleanup) = cleanup {
281                    propagate(cleanup, exit_state);
282                }
283
284                if !return_.is_empty() {
285                    analysis.apply_call_return_effect(exit_state, block, place);
286                    for &target in return_ {
287                        propagate(target, exit_state);
288                    }
289                }
290            }
291            TerminatorEdges::SwitchInt { targets, discr } => {
292                if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
293                    let mut tmp = analysis.bottom_value(body);
294                    for (value, target) in targets.iter() {
295                        tmp.clone_from(&exit_state);
296                        analysis.apply_switch_int_edge_effect(
297                            &mut data,
298                            &mut tmp,
299                            SwitchIntTarget { value: Some(value), target },
300                        );
301                        propagate(target, &tmp);
302                    }
303
304                    // Once we get to the final, "otherwise" branch, there is no need to preserve
305                    // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
306                    // a clone of the dataflow state.
307                    let otherwise = targets.otherwise();
308                    analysis.apply_switch_int_edge_effect(
309                        &mut data,
310                        exit_state,
311                        SwitchIntTarget { value: None, target: otherwise },
312                    );
313                    propagate(otherwise, exit_state);
314                } else {
315                    for target in targets.all_targets() {
316                        propagate(*target, exit_state);
317                    }
318                }
319            }
320        }
321    }
322
323    fn apply_effects_in_range<'tcx, A>(
324        analysis: &mut A,
325        state: &mut A::Domain,
326        block: BasicBlock,
327        block_data: &mir::BasicBlockData<'tcx>,
328        effects: RangeInclusive<EffectIndex>,
329    ) where
330        A: Analysis<'tcx>,
331    {
332        let (from, to) = (*effects.start(), *effects.end());
333        let terminator_index = block_data.statements.len();
334
335        assert!(to.statement_index <= terminator_index);
336        assert!(!to.precedes_in_forward_order(from));
337
338        // If we have applied the before affect of the statement or terminator at `from` but not its
339        // after effect, do so now and start the loop below from the next statement.
340
341        let first_unapplied_index = match from.effect {
342            Effect::Early => from.statement_index,
343
344            Effect::Primary if from.statement_index == terminator_index => {
345                debug_assert_eq!(from, to);
346
347                let location = Location { block, statement_index: terminator_index };
348                let terminator = block_data.terminator();
349                analysis.apply_primary_terminator_effect(state, terminator, location);
350                return;
351            }
352
353            Effect::Primary => {
354                let location = Location { block, statement_index: from.statement_index };
355                let statement = &block_data.statements[from.statement_index];
356                analysis.apply_primary_statement_effect(state, statement, location);
357
358                // If we only needed to apply the after effect of the statement at `idx`, we are
359                // done.
360                if from == to {
361                    return;
362                }
363
364                from.statement_index + 1
365            }
366        };
367
368        // Handle all statements between `from` and `to` whose effects must be applied in full.
369
370        for statement_index in first_unapplied_index..to.statement_index {
371            let location = Location { block, statement_index };
372            let statement = &block_data.statements[statement_index];
373            analysis.apply_early_statement_effect(state, statement, location);
374            analysis.apply_primary_statement_effect(state, statement, location);
375        }
376
377        // Handle the statement or terminator at `to`.
378
379        let location = Location { block, statement_index: to.statement_index };
380        if to.statement_index == terminator_index {
381            let terminator = block_data.terminator();
382            analysis.apply_early_terminator_effect(state, terminator, location);
383
384            if to.effect == Effect::Primary {
385                analysis.apply_primary_terminator_effect(state, terminator, location);
386            }
387        } else {
388            let statement = &block_data.statements[to.statement_index];
389            analysis.apply_early_statement_effect(state, statement, location);
390
391            if to.effect == Effect::Primary {
392                analysis.apply_primary_statement_effect(state, statement, location);
393            }
394        }
395    }
396
397    fn visit_results_in_block<'mir, 'tcx, A>(
398        state: &mut A::Domain,
399        block: BasicBlock,
400        block_data: &'mir mir::BasicBlockData<'tcx>,
401        results: &mut Results<'tcx, A>,
402        vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
403    ) where
404        A: Analysis<'tcx>,
405    {
406        state.clone_from(results.entry_set_for_block(block));
407
408        vis.visit_block_start(state);
409
410        for (statement_index, stmt) in block_data.statements.iter().enumerate() {
411            let loc = Location { block, statement_index };
412            results.analysis.apply_early_statement_effect(state, stmt, loc);
413            vis.visit_after_early_statement_effect(results, state, stmt, loc);
414            results.analysis.apply_primary_statement_effect(state, stmt, loc);
415            vis.visit_after_primary_statement_effect(results, state, stmt, loc);
416        }
417
418        let loc = Location { block, statement_index: block_data.statements.len() };
419        let term = block_data.terminator();
420        results.analysis.apply_early_terminator_effect(state, term, loc);
421        vis.visit_after_early_terminator_effect(results, state, term, loc);
422        results.analysis.apply_primary_terminator_effect(state, term, loc);
423        vis.visit_after_primary_terminator_effect(results, state, term, loc);
424
425        vis.visit_block_end(state);
426    }
427}