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