Skip to main content

rustc_mir_dataflow/framework/
direction.rs

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