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 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 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 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
51pub 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 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 let next_effect = match from.effect {
151 _ 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 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 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 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
242pub 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 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 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 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 from == to {
356 return;
357 }
358
359 from.statement_index + 1
360 }
361 };
362
363 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 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}