rustc_mir_dataflow/framework/
direction.rs1use rustc_middle::bug;
2use rustc_middle::mir::{self, BasicBlock, 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 fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex;
14
15 fn next_index(idx: EffectIndex) -> EffectIndex;
17
18 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 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
42pub 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 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().edges() {
83 TerminatorEdges::AssignOnReturn { return_, place, .. }
85 if return_.contains(&block) =>
86 {
87 let mut tmp = exit_state.clone();
88 analysis.apply_call_return_effect(&mut tmp, pred, place);
89 propagate(pred, &tmp);
90 }
91
92 TerminatorEdges::SwitchInt { targets, discr } => {
93 if let Some(_data) = analysis.get_switch_int_data(pred, targets, discr) {
94 ::rustc_middle::util::bug::bug_fmt(format_args!("SwitchInt edge effects are unsupported in backward dataflow analyses"));bug!(
95 "SwitchInt edge effects are unsupported in backward dataflow analyses"
96 );
97 } else {
98 propagate(pred, exit_state)
99 }
100 }
101
102 _ => propagate(pred, exit_state),
103 }
104 }
105 }
106
107 fn visit_results_in_block<'mir, 'tcx, A>(
108 analysis: &A,
109 state: &mut A::Domain,
110 block: BasicBlock,
111 block_data: &'mir mir::BasicBlockData<'tcx>,
112 vis: &mut impl ResultsVisitor<'tcx, A>,
113 ) where
114 A: Analysis<'tcx>,
115 {
116 let loc = Location { block, statement_index: block_data.statements.len() };
117 let term = block_data.terminator();
118 analysis.apply_early_terminator_effect(state, term, loc);
119 vis.visit_after_early_terminator_effect(state, term, loc);
120 analysis.apply_primary_terminator_effect(state, term, loc);
121 vis.visit_after_primary_terminator_effect(state, term, loc);
122
123 for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
124 let loc = Location { block, statement_index };
125 analysis.apply_early_statement_effect(state, stmt, loc);
126 vis.visit_after_early_statement_effect(state, stmt, loc);
127 analysis.apply_primary_statement_effect(state, stmt, loc);
128 vis.visit_after_primary_statement_effect(state, stmt, loc);
129 }
130 }
131}
132
133pub struct Forward;
135
136impl Direction for Forward {
137 const IS_FORWARD: bool = true;
138
139 fn first_index(_block_data: &mir::BasicBlockData<'_>) -> EffectIndex {
140 Effect::Early.at_index(0)
141 }
142
143 fn next_index(idx: EffectIndex) -> EffectIndex {
145 match idx.effect {
146 Effect::Early => Effect::Primary.at_index(idx.statement_index),
147 Effect::Primary => Effect::Early.at_index(idx.statement_index + 1),
148 }
149 }
150
151 fn apply_effects_in_block<'mir, 'tcx, A>(
152 analysis: &A,
153 body: &mir::Body<'tcx>,
154 state: &mut A::Domain,
155 block: BasicBlock,
156 block_data: &'mir mir::BasicBlockData<'tcx>,
157 mut propagate: impl FnMut(BasicBlock, &A::Domain),
158 ) where
159 A: Analysis<'tcx>,
160 {
161 for (statement_index, statement) in block_data.statements.iter().enumerate() {
162 let location = Location { block, statement_index };
163 analysis.apply_early_statement_effect(state, statement, location);
164 analysis.apply_primary_statement_effect(state, statement, location);
165 }
166 let terminator = block_data.terminator();
167 let location = Location { block, statement_index: block_data.statements.len() };
168 analysis.apply_early_terminator_effect(state, terminator, location);
169 let edges = analysis.get_terminator_edges(state, terminator, location);
171 analysis.apply_primary_terminator_effect(state, terminator, location);
172
173 let exit_state = state;
174 match edges {
175 TerminatorEdges::None => {}
176 TerminatorEdges::Single(target) => propagate(target, exit_state),
177 TerminatorEdges::Double(target, unwind) => {
178 propagate(target, exit_state);
179 propagate(unwind, exit_state);
180 }
181 TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
182 if let Some(cleanup) = cleanup {
184 propagate(cleanup, exit_state);
185 }
186
187 if !return_.is_empty() {
188 analysis.apply_call_return_effect(exit_state, block, place);
189 for target in return_ {
190 propagate(target, exit_state);
191 }
192 }
193 }
194 TerminatorEdges::SwitchInt { targets, discr } => {
195 if let Some(data) = analysis.get_switch_int_data(block, targets, discr) {
196 let mut tmp = analysis.bottom_value(body);
197 for (i, (_value, target)) in targets.iter().enumerate() {
198 tmp.clone_from(exit_state);
199 let target_idx = SwitchTargetIndex::Normal(i);
200 analysis.apply_switch_int_edge_effect(&mut tmp, &data, target_idx);
201 propagate(target, &tmp);
202 }
203
204 analysis.apply_switch_int_edge_effect(
208 exit_state,
209 &data,
210 SwitchTargetIndex::Otherwise,
211 );
212 propagate(targets.otherwise(), exit_state);
213 } else {
214 for target in targets.all_targets() {
215 propagate(*target, exit_state);
216 }
217 }
218 }
219 }
220 }
221
222 fn visit_results_in_block<'mir, 'tcx, A>(
223 analysis: &A,
224 state: &mut A::Domain,
225 block: BasicBlock,
226 block_data: &'mir mir::BasicBlockData<'tcx>,
227 vis: &mut impl ResultsVisitor<'tcx, A>,
228 ) where
229 A: Analysis<'tcx>,
230 {
231 for (statement_index, stmt) in block_data.statements.iter().enumerate() {
232 let loc = Location { block, statement_index };
233 analysis.apply_early_statement_effect(state, stmt, loc);
234 vis.visit_after_early_statement_effect(state, stmt, loc);
235 analysis.apply_primary_statement_effect(state, stmt, loc);
236 vis.visit_after_primary_statement_effect(state, stmt, loc);
237 }
238
239 let loc = Location { block, statement_index: block_data.statements.len() };
240 let term = block_data.terminator();
241 analysis.apply_early_terminator_effect(state, term, loc);
242 vis.visit_after_early_terminator_effect(state, term, loc);
243 analysis.apply_primary_terminator_effect(state, term, loc);
244 vis.visit_after_primary_terminator_effect(state, term, loc);
245 }
246}