1use std::ops::RangeInclusive;
2
3use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
4
5use super::visitor::ResultsVisitor;
6use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
7
8pub trait Direction {
9 const IS_FORWARD: bool;
10 const IS_BACKWARD: bool = !Self::IS_FORWARD;
11
12 fn apply_effects_in_block<'mir, 'tcx, A>(
14 analysis: &mut A,
15 body: &mir::Body<'tcx>,
16 state: &mut A::Domain,
17 block: BasicBlock,
18 block_data: &'mir mir::BasicBlockData<'tcx>,
19 propagate: impl FnMut(BasicBlock, &A::Domain),
20 ) where
21 A: Analysis<'tcx>;
22
23 fn apply_effects_in_range<'tcx, A>(
28 analysis: &mut A,
29 state: &mut A::Domain,
30 block: BasicBlock,
31 block_data: &mir::BasicBlockData<'tcx>,
32 effects: RangeInclusive<EffectIndex>,
33 ) where
34 A: Analysis<'tcx>;
35
36 fn visit_results_in_block<'mir, 'tcx, A>(
40 state: &mut A::Domain,
41 block: BasicBlock,
42 block_data: &'mir mir::BasicBlockData<'tcx>,
43 results: &mut Results<'tcx, A>,
44 vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
45 ) where
46 A: Analysis<'tcx>;
47}
48
49pub struct Backward;
51
52impl Direction for Backward {
53 const IS_FORWARD: bool = false;
54
55 fn apply_effects_in_block<'mir, 'tcx, A>(
56 analysis: &mut A,
57 body: &mir::Body<'tcx>,
58 state: &mut A::Domain,
59 block: BasicBlock,
60 block_data: &'mir mir::BasicBlockData<'tcx>,
61 mut propagate: impl FnMut(BasicBlock, &A::Domain),
62 ) where
63 A: Analysis<'tcx>,
64 {
65 let terminator = block_data.terminator();
66 let location = Location { block, statement_index: block_data.statements.len() };
67 analysis.apply_early_terminator_effect(state, terminator, location);
68 analysis.apply_primary_terminator_effect(state, terminator, location);
69 for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
70 let location = Location { block, statement_index };
71 analysis.apply_early_statement_effect(state, statement, location);
72 analysis.apply_primary_statement_effect(state, statement, location);
73 }
74
75 let exit_state = state;
76 for pred in body.basic_blocks.predecessors()[block].iter().copied() {
77 match body[pred].terminator().kind {
78 mir::TerminatorKind::Call { destination, target: Some(dest), .. }
80 if dest == block =>
81 {
82 let mut tmp = exit_state.clone();
83 analysis.apply_call_return_effect(
84 &mut tmp,
85 pred,
86 CallReturnPlaces::Call(destination),
87 );
88 propagate(pred, &tmp);
89 }
90
91 mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
92 if targets.contains(&block) =>
93 {
94 let mut tmp = exit_state.clone();
95 analysis.apply_call_return_effect(
96 &mut tmp,
97 pred,
98 CallReturnPlaces::InlineAsm(operands),
99 );
100 propagate(pred, &tmp);
101 }
102
103 mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => {
104 let mut tmp = exit_state.clone();
105 analysis.apply_call_return_effect(
106 &mut tmp,
107 resume,
108 CallReturnPlaces::Yield(resume_arg),
109 );
110 propagate(pred, &tmp);
111 }
112
113 mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
114 if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
115 let values = &body.basic_blocks.switch_sources()[&(block, pred)];
116 let targets =
117 values.iter().map(|&value| SwitchIntTarget { value, target: block });
118
119 let mut tmp = analysis.bottom_value(body);
120 for target in targets {
121 tmp.clone_from(&exit_state);
122 analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
123 propagate(pred, &tmp);
124 }
125 } else {
126 propagate(pred, exit_state)
127 }
128 }
129
130 _ => propagate(pred, exit_state),
131 }
132 }
133 }
134
135 fn apply_effects_in_range<'tcx, A>(
136 analysis: &mut A,
137 state: &mut A::Domain,
138 block: BasicBlock,
139 block_data: &mir::BasicBlockData<'tcx>,
140 effects: RangeInclusive<EffectIndex>,
141 ) where
142 A: Analysis<'tcx>,
143 {
144 let (from, to) = (*effects.start(), *effects.end());
145 let terminator_index = block_data.statements.len();
146
147 assert!(from.statement_index <= terminator_index);
148 assert!(!to.precedes_in_backward_order(from));
149
150 let next_effect = match from.effect {
153 _ if from.statement_index == terminator_index => {
155 let location = Location { block, statement_index: from.statement_index };
156 let terminator = block_data.terminator();
157
158 if from.effect == Effect::Early {
159 analysis.apply_early_terminator_effect(state, terminator, location);
160 if to == Effect::Early.at_index(terminator_index) {
161 return;
162 }
163 }
164
165 analysis.apply_primary_terminator_effect(state, terminator, location);
166 if to == Effect::Primary.at_index(terminator_index) {
167 return;
168 }
169
170 from.statement_index - 1
173 }
174
175 Effect::Primary => {
176 let location = Location { block, statement_index: from.statement_index };
177 let statement = &block_data.statements[from.statement_index];
178
179 analysis.apply_primary_statement_effect(state, statement, location);
180 if to == Effect::Primary.at_index(from.statement_index) {
181 return;
182 }
183
184 from.statement_index - 1
185 }
186
187 Effect::Early => from.statement_index,
188 };
189
190 for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
193 let location = Location { block, statement_index };
194 let statement = &block_data.statements[statement_index];
195 analysis.apply_early_statement_effect(state, statement, location);
196 analysis.apply_primary_statement_effect(state, statement, location);
197 }
198
199 let location = Location { block, statement_index: to.statement_index };
202 let statement = &block_data.statements[to.statement_index];
203 analysis.apply_early_statement_effect(state, statement, location);
204
205 if to.effect == Effect::Early {
206 return;
207 }
208
209 analysis.apply_primary_statement_effect(state, statement, location);
210 }
211
212 fn visit_results_in_block<'mir, 'tcx, A>(
213 state: &mut A::Domain,
214 block: BasicBlock,
215 block_data: &'mir mir::BasicBlockData<'tcx>,
216 results: &mut Results<'tcx, A>,
217 vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
218 ) where
219 A: Analysis<'tcx>,
220 {
221 state.clone_from(results.entry_set_for_block(block));
222
223 vis.visit_block_end(state);
224
225 let loc = Location { block, statement_index: block_data.statements.len() };
226 let term = block_data.terminator();
227 results.analysis.apply_early_terminator_effect(state, term, loc);
228 vis.visit_after_early_terminator_effect(results, state, term, loc);
229 results.analysis.apply_primary_terminator_effect(state, term, loc);
230 vis.visit_after_primary_terminator_effect(results, state, term, loc);
231
232 for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
233 let loc = Location { block, statement_index };
234 results.analysis.apply_early_statement_effect(state, stmt, loc);
235 vis.visit_after_early_statement_effect(results, state, stmt, loc);
236 results.analysis.apply_primary_statement_effect(state, stmt, loc);
237 vis.visit_after_primary_statement_effect(results, state, stmt, loc);
238 }
239
240 vis.visit_block_start(state);
241 }
242}
243
244pub struct Forward;
246
247impl Direction for Forward {
248 const IS_FORWARD: bool = true;
249
250 fn apply_effects_in_block<'mir, 'tcx, A>(
251 analysis: &mut A,
252 body: &mir::Body<'tcx>,
253 state: &mut A::Domain,
254 block: BasicBlock,
255 block_data: &'mir mir::BasicBlockData<'tcx>,
256 mut propagate: impl FnMut(BasicBlock, &A::Domain),
257 ) where
258 A: Analysis<'tcx>,
259 {
260 for (statement_index, statement) in block_data.statements.iter().enumerate() {
261 let location = Location { block, statement_index };
262 analysis.apply_early_statement_effect(state, statement, location);
263 analysis.apply_primary_statement_effect(state, statement, location);
264 }
265 let terminator = block_data.terminator();
266 let location = Location { block, statement_index: block_data.statements.len() };
267 analysis.apply_early_terminator_effect(state, terminator, location);
268 let edges = analysis.apply_primary_terminator_effect(state, terminator, location);
269
270 let exit_state = state;
271 match edges {
272 TerminatorEdges::None => {}
273 TerminatorEdges::Single(target) => propagate(target, exit_state),
274 TerminatorEdges::Double(target, unwind) => {
275 propagate(target, exit_state);
276 propagate(unwind, exit_state);
277 }
278 TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
279 if let Some(cleanup) = cleanup {
281 propagate(cleanup, exit_state);
282 }
283
284 if !return_.is_empty() {
285 analysis.apply_call_return_effect(exit_state, block, place);
286 for &target in return_ {
287 propagate(target, exit_state);
288 }
289 }
290 }
291 TerminatorEdges::SwitchInt { targets, discr } => {
292 if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
293 let mut tmp = analysis.bottom_value(body);
294 for (value, target) in targets.iter() {
295 tmp.clone_from(&exit_state);
296 analysis.apply_switch_int_edge_effect(
297 &mut data,
298 &mut tmp,
299 SwitchIntTarget { value: Some(value), target },
300 );
301 propagate(target, &tmp);
302 }
303
304 let otherwise = targets.otherwise();
308 analysis.apply_switch_int_edge_effect(
309 &mut data,
310 exit_state,
311 SwitchIntTarget { value: None, target: otherwise },
312 );
313 propagate(otherwise, exit_state);
314 } else {
315 for target in targets.all_targets() {
316 propagate(*target, exit_state);
317 }
318 }
319 }
320 }
321 }
322
323 fn apply_effects_in_range<'tcx, A>(
324 analysis: &mut A,
325 state: &mut A::Domain,
326 block: BasicBlock,
327 block_data: &mir::BasicBlockData<'tcx>,
328 effects: RangeInclusive<EffectIndex>,
329 ) where
330 A: Analysis<'tcx>,
331 {
332 let (from, to) = (*effects.start(), *effects.end());
333 let terminator_index = block_data.statements.len();
334
335 assert!(to.statement_index <= terminator_index);
336 assert!(!to.precedes_in_forward_order(from));
337
338 let first_unapplied_index = match from.effect {
342 Effect::Early => from.statement_index,
343
344 Effect::Primary if from.statement_index == terminator_index => {
345 debug_assert_eq!(from, to);
346
347 let location = Location { block, statement_index: terminator_index };
348 let terminator = block_data.terminator();
349 analysis.apply_primary_terminator_effect(state, terminator, location);
350 return;
351 }
352
353 Effect::Primary => {
354 let location = Location { block, statement_index: from.statement_index };
355 let statement = &block_data.statements[from.statement_index];
356 analysis.apply_primary_statement_effect(state, statement, location);
357
358 if from == to {
361 return;
362 }
363
364 from.statement_index + 1
365 }
366 };
367
368 for statement_index in first_unapplied_index..to.statement_index {
371 let location = Location { block, statement_index };
372 let statement = &block_data.statements[statement_index];
373 analysis.apply_early_statement_effect(state, statement, location);
374 analysis.apply_primary_statement_effect(state, statement, location);
375 }
376
377 let location = Location { block, statement_index: to.statement_index };
380 if to.statement_index == terminator_index {
381 let terminator = block_data.terminator();
382 analysis.apply_early_terminator_effect(state, terminator, location);
383
384 if to.effect == Effect::Primary {
385 analysis.apply_primary_terminator_effect(state, terminator, location);
386 }
387 } else {
388 let statement = &block_data.statements[to.statement_index];
389 analysis.apply_early_statement_effect(state, statement, location);
390
391 if to.effect == Effect::Primary {
392 analysis.apply_primary_statement_effect(state, statement, location);
393 }
394 }
395 }
396
397 fn visit_results_in_block<'mir, 'tcx, A>(
398 state: &mut A::Domain,
399 block: BasicBlock,
400 block_data: &'mir mir::BasicBlockData<'tcx>,
401 results: &mut Results<'tcx, A>,
402 vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
403 ) where
404 A: Analysis<'tcx>,
405 {
406 state.clone_from(results.entry_set_for_block(block));
407
408 vis.visit_block_start(state);
409
410 for (statement_index, stmt) in block_data.statements.iter().enumerate() {
411 let loc = Location { block, statement_index };
412 results.analysis.apply_early_statement_effect(state, stmt, loc);
413 vis.visit_after_early_statement_effect(results, state, stmt, loc);
414 results.analysis.apply_primary_statement_effect(state, stmt, loc);
415 vis.visit_after_primary_statement_effect(results, state, stmt, loc);
416 }
417
418 let loc = Location { block, statement_index: block_data.statements.len() };
419 let term = block_data.terminator();
420 results.analysis.apply_early_terminator_effect(state, term, loc);
421 vis.visit_after_early_terminator_effect(results, state, term, loc);
422 results.analysis.apply_primary_terminator_effect(state, term, loc);
423 vis.visit_after_primary_terminator_effect(results, state, term, loc);
424
425 vis.visit_block_end(state);
426 }
427}