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(analysis, state, term, loc);
148        analysis.apply_primary_terminator_effect(state, term, loc);
149        vis.visit_after_primary_terminator_effect(analysis, 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(analysis, state, stmt, loc);
155            analysis.apply_primary_statement_effect(state, stmt, loc);
156            vis.visit_after_primary_statement_effect(analysis, 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        let edges = analysis.apply_primary_terminator_effect(state, terminator, location);
198
199        let exit_state = state;
200        match edges {
201            TerminatorEdges::None => {}
202            TerminatorEdges::Single(target) => propagate(target, exit_state),
203            TerminatorEdges::Double(target, unwind) => {
204                propagate(target, exit_state);
205                propagate(unwind, exit_state);
206            }
207            TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
208                // This must be done *first*, otherwise the unwind path will see the assignments.
209                if let Some(cleanup) = cleanup {
210                    propagate(cleanup, exit_state);
211                }
212
213                if !return_.is_empty() {
214                    analysis.apply_call_return_effect(exit_state, block, place);
215                    for target in return_ {
216                        propagate(target, exit_state);
217                    }
218                }
219            }
220            TerminatorEdges::SwitchInt { targets, discr } => {
221                if let Some(mut data) = analysis.get_switch_int_data(block, targets, discr) {
222                    let mut tmp = analysis.bottom_value(body);
223                    for (i, (_value, target)) in targets.iter().enumerate() {
224                        tmp.clone_from(exit_state);
225                        let target_idx = SwitchTargetIndex::Normal(i);
226                        analysis.apply_switch_int_edge_effect(&mut tmp, &mut data, target_idx);
227                        propagate(target, &tmp);
228                    }
229
230                    // Once we get to the final, "otherwise" branch, there is no need to preserve
231                    // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
232                    // a clone of the dataflow state.
233                    analysis.apply_switch_int_edge_effect(
234                        exit_state,
235                        &mut data,
236                        SwitchTargetIndex::Otherwise,
237                    );
238                    propagate(targets.otherwise(), exit_state);
239                } else {
240                    for target in targets.all_targets() {
241                        propagate(*target, exit_state);
242                    }
243                }
244            }
245        }
246    }
247
248    fn visit_results_in_block<'mir, 'tcx, A>(
249        analysis: &A,
250        state: &mut A::Domain,
251        block: BasicBlock,
252        block_data: &'mir mir::BasicBlockData<'tcx>,
253        vis: &mut impl ResultsVisitor<'tcx, A>,
254    ) where
255        A: Analysis<'tcx>,
256    {
257        for (statement_index, stmt) in block_data.statements.iter().enumerate() {
258            let loc = Location { block, statement_index };
259            analysis.apply_early_statement_effect(state, stmt, loc);
260            vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
261            analysis.apply_primary_statement_effect(state, stmt, loc);
262            vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
263        }
264
265        let loc = Location { block, statement_index: block_data.statements.len() };
266        let term = block_data.terminator();
267        analysis.apply_early_terminator_effect(state, term, loc);
268        vis.visit_after_early_terminator_effect(analysis, state, term, loc);
269        analysis.apply_primary_terminator_effect(state, term, loc);
270        vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
271    }
272}