Skip to main content

rustc_mir_dataflow/framework/
direction.rs

1use rustc_middle::bug;
2use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
3
4use super::visitor::ResultsVisitor;
5use super::{Analysis, Effect, EffectIndex, SwitchTargetIndex};
6
7pub trait Direction {
8    const IS_FORWARD: bool;
9    const IS_BACKWARD: bool = !Self::IS_FORWARD;
10
11    /// Returns the first statement index for this direction. (0 when going forward and
12    /// `statements.len()` when going backward.)
13    fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex;
14
15    /// Returns the next index for this direction.
16    fn next_index(idx: EffectIndex) -> EffectIndex;
17
18    /// Called by `iterate_to_fixpoint` during initial analysis computation.
19    fn apply_effects_in_block<'mir, 'tcx, A>(
20        analysis: &A,
21        body: &mir::Body<'tcx>,
22        state: &mut A::Domain,
23        block: BasicBlock,
24        block_data: &'mir mir::BasicBlockData<'tcx>,
25        propagate: impl FnMut(BasicBlock, &A::Domain),
26    ) where
27        A: Analysis<'tcx>;
28
29    /// Called by `ResultsVisitor` to recompute the analysis domain values for
30    /// all locations in a basic block (starting from `entry_state` and to
31    /// visit them with `vis`.
32    fn visit_results_in_block<'mir, 'tcx, A>(
33        analysis: &A,
34        state: &mut A::Domain,
35        block: BasicBlock,
36        block_data: &'mir mir::BasicBlockData<'tcx>,
37        vis: &mut impl ResultsVisitor<'tcx, A>,
38    ) where
39        A: Analysis<'tcx>;
40}
41
42/// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement).
43pub struct Backward;
44
45impl Direction for Backward {
46    const IS_FORWARD: bool = false;
47
48    fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex {
49        Effect::Early.at_index(block_data.statements.len())
50    }
51
52    /// Returns the next index for this direction.
53    fn next_index(idx: EffectIndex) -> EffectIndex {
54        match idx.effect {
55            Effect::Early => Effect::Primary.at_index(idx.statement_index),
56            Effect::Primary => Effect::Early.at_index(idx.statement_index - 1),
57        }
58    }
59
60    fn apply_effects_in_block<'mir, 'tcx, A>(
61        analysis: &A,
62        body: &mir::Body<'tcx>,
63        state: &mut A::Domain,
64        block: BasicBlock,
65        block_data: &'mir mir::BasicBlockData<'tcx>,
66        mut propagate: impl FnMut(BasicBlock, &A::Domain),
67    ) where
68        A: Analysis<'tcx>,
69    {
70        let terminator = block_data.terminator();
71        let location = Location { block, statement_index: block_data.statements.len() };
72        analysis.apply_early_terminator_effect(state, terminator, location);
73        analysis.apply_primary_terminator_effect(state, terminator, location);
74        for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
75            let location = Location { block, statement_index };
76            analysis.apply_early_statement_effect(state, statement, location);
77            analysis.apply_primary_statement_effect(state, statement, location);
78        }
79
80        let exit_state = state;
81        for pred in body.basic_blocks.predecessors()[block].iter().copied() {
82            match body[pred].terminator().kind {
83                // Apply terminator-specific edge effects.
84                mir::TerminatorKind::Call { destination, target: Some(dest), .. }
85                    if dest == block =>
86                {
87                    let mut tmp = exit_state.clone();
88                    analysis.apply_call_return_effect(
89                        &mut tmp,
90                        pred,
91                        CallReturnPlaces::Call(destination),
92                    );
93                    propagate(pred, &tmp);
94                }
95
96                mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
97                    if targets.contains(&block) =>
98                {
99                    let mut tmp = exit_state.clone();
100                    analysis.apply_call_return_effect(
101                        &mut tmp,
102                        pred,
103                        CallReturnPlaces::InlineAsm(operands),
104                    );
105                    propagate(pred, &tmp);
106                }
107
108                mir::TerminatorKind::Yield { resume, drop, resume_arg, .. }
109                    if resume == block || drop == Some(block) =>
110                {
111                    let mut tmp = exit_state.clone();
112                    analysis.apply_call_return_effect(
113                        &mut tmp,
114                        block,
115                        CallReturnPlaces::Yield(resume_arg),
116                    );
117                    propagate(pred, &tmp);
118                }
119
120                mir::TerminatorKind::SwitchInt { ref targets, ref discr } => {
121                    if let Some(_data) = analysis.get_switch_int_data(pred, targets, discr) {
122                        ::rustc_middle::util::bug::bug_fmt(format_args!("SwitchInt edge effects are unsupported in backward dataflow analyses"));bug!(
123                            "SwitchInt edge effects are unsupported in backward dataflow analyses"
124                        );
125                    } else {
126                        propagate(pred, exit_state)
127                    }
128                }
129
130                _ => propagate(pred, exit_state),
131            }
132        }
133    }
134
135    fn visit_results_in_block<'mir, 'tcx, A>(
136        analysis: &A,
137        state: &mut A::Domain,
138        block: BasicBlock,
139        block_data: &'mir mir::BasicBlockData<'tcx>,
140        vis: &mut impl ResultsVisitor<'tcx, A>,
141    ) where
142        A: Analysis<'tcx>,
143    {
144        let loc = Location { block, statement_index: block_data.statements.len() };
145        let term = block_data.terminator();
146        analysis.apply_early_terminator_effect(state, term, loc);
147        vis.visit_after_early_terminator_effect(state, term, loc);
148        analysis.apply_primary_terminator_effect(state, term, loc);
149        vis.visit_after_primary_terminator_effect(state, term, loc);
150
151        for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
152            let loc = Location { block, statement_index };
153            analysis.apply_early_statement_effect(state, stmt, loc);
154            vis.visit_after_early_statement_effect(state, stmt, loc);
155            analysis.apply_primary_statement_effect(state, stmt, loc);
156            vis.visit_after_primary_statement_effect(state, stmt, loc);
157        }
158    }
159}
160
161/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
162pub struct Forward;
163
164impl Direction for Forward {
165    const IS_FORWARD: bool = true;
166
167    fn first_index(_block_data: &mir::BasicBlockData<'_>) -> EffectIndex {
168        Effect::Early.at_index(0)
169    }
170
171    /// Returns the next index for this direction.
172    fn next_index(idx: EffectIndex) -> EffectIndex {
173        match idx.effect {
174            Effect::Early => Effect::Primary.at_index(idx.statement_index),
175            Effect::Primary => Effect::Early.at_index(idx.statement_index + 1),
176        }
177    }
178
179    fn apply_effects_in_block<'mir, 'tcx, A>(
180        analysis: &A,
181        body: &mir::Body<'tcx>,
182        state: &mut A::Domain,
183        block: BasicBlock,
184        block_data: &'mir mir::BasicBlockData<'tcx>,
185        mut propagate: impl FnMut(BasicBlock, &A::Domain),
186    ) where
187        A: Analysis<'tcx>,
188    {
189        for (statement_index, statement) in block_data.statements.iter().enumerate() {
190            let location = Location { block, statement_index };
191            analysis.apply_early_statement_effect(state, statement, location);
192            analysis.apply_primary_statement_effect(state, statement, location);
193        }
194        let terminator = block_data.terminator();
195        let location = Location { block, statement_index: block_data.statements.len() };
196        analysis.apply_early_terminator_effect(state, terminator, location);
197        // Edges are obtained *before* calling `apply_primary_terminator_effect`.
198        let edges = analysis.get_terminator_edges(state, terminator, location);
199        analysis.apply_primary_terminator_effect(state, terminator, location);
200
201        let exit_state = state;
202        match edges {
203            TerminatorEdges::None => {}
204            TerminatorEdges::Single(target) => propagate(target, exit_state),
205            TerminatorEdges::Double(target, unwind) => {
206                propagate(target, exit_state);
207                propagate(unwind, exit_state);
208            }
209            TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
210                // This must be done *first*, otherwise the unwind path will see the assignments.
211                if let Some(cleanup) = cleanup {
212                    propagate(cleanup, exit_state);
213                }
214
215                if !return_.is_empty() {
216                    analysis.apply_call_return_effect(exit_state, block, place);
217                    for target in return_ {
218                        propagate(target, exit_state);
219                    }
220                }
221            }
222            TerminatorEdges::SwitchInt { targets, discr } => {
223                if let Some(mut data) = analysis.get_switch_int_data(block, targets, discr) {
224                    let mut tmp = analysis.bottom_value(body);
225                    for (i, (_value, target)) in targets.iter().enumerate() {
226                        tmp.clone_from(exit_state);
227                        let target_idx = SwitchTargetIndex::Normal(i);
228                        analysis.apply_switch_int_edge_effect(&mut tmp, &mut data, target_idx);
229                        propagate(target, &tmp);
230                    }
231
232                    // Once we get to the final, "otherwise" branch, there is no need to preserve
233                    // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
234                    // a clone of the dataflow state.
235                    analysis.apply_switch_int_edge_effect(
236                        exit_state,
237                        &mut data,
238                        SwitchTargetIndex::Otherwise,
239                    );
240                    propagate(targets.otherwise(), exit_state);
241                } else {
242                    for target in targets.all_targets() {
243                        propagate(*target, exit_state);
244                    }
245                }
246            }
247        }
248    }
249
250    fn visit_results_in_block<'mir, 'tcx, A>(
251        analysis: &A,
252        state: &mut A::Domain,
253        block: BasicBlock,
254        block_data: &'mir mir::BasicBlockData<'tcx>,
255        vis: &mut impl ResultsVisitor<'tcx, A>,
256    ) where
257        A: Analysis<'tcx>,
258    {
259        for (statement_index, stmt) in block_data.statements.iter().enumerate() {
260            let loc = Location { block, statement_index };
261            analysis.apply_early_statement_effect(state, stmt, loc);
262            vis.visit_after_early_statement_effect(state, stmt, loc);
263            analysis.apply_primary_statement_effect(state, stmt, loc);
264            vis.visit_after_primary_statement_effect(state, stmt, loc);
265        }
266
267        let loc = Location { block, statement_index: block_data.statements.len() };
268        let term = block_data.terminator();
269        analysis.apply_early_terminator_effect(state, term, loc);
270        vis.visit_after_early_terminator_effect(state, term, loc);
271        analysis.apply_primary_terminator_effect(state, term, loc);
272        vis.visit_after_primary_terminator_effect(state, term, loc);
273    }
274}