rustc_mir_dataflow/framework/
direction.rs

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