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, resume_arg, .. } if resume == block => {
105 let mut tmp = exit_state.clone();
106 analysis.apply_call_return_effect(
107 &mut tmp,
108 resume,
109 CallReturnPlaces::Yield(resume_arg),
110 );
111 propagate(pred, &tmp);
112 }
113
114 mir::TerminatorKind::SwitchInt { ref targets, ref discr } => {
115 if let Some(_data) = analysis.get_switch_int_data(pred, targets, discr) {
116 ::rustc_middle::util::bug::bug_fmt(format_args!("SwitchInt edge effects are unsupported in backward dataflow analyses"));bug!(
117 "SwitchInt edge effects are unsupported in backward dataflow analyses"
118 );
119 } else {
120 propagate(pred, exit_state)
121 }
122 }
123
124 _ => propagate(pred, exit_state),
125 }
126 }
127 }
128
129 fn apply_effects_in_range<'tcx, A>(
130 analysis: &A,
131 state: &mut A::Domain,
132 block: BasicBlock,
133 block_data: &mir::BasicBlockData<'tcx>,
134 effects: RangeInclusive<EffectIndex>,
135 ) where
136 A: Analysis<'tcx>,
137 {
138 let (from, to) = (*effects.start(), *effects.end());
139 let terminator_index = block_data.statements.len();
140
141 if !(from.statement_index <= terminator_index) {
::core::panicking::panic("assertion failed: from.statement_index <= terminator_index")
};assert!(from.statement_index <= terminator_index);
142 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));
143
144 let next_effect = match from.effect {
147 _ if from.statement_index == terminator_index => {
149 let location = Location { block, statement_index: from.statement_index };
150 let terminator = block_data.terminator();
151
152 if from.effect == Effect::Early {
153 analysis.apply_early_terminator_effect(state, terminator, location);
154 if to == Effect::Early.at_index(terminator_index) {
155 return;
156 }
157 }
158
159 analysis.apply_primary_terminator_effect(state, terminator, location);
160 if to == Effect::Primary.at_index(terminator_index) {
161 return;
162 }
163
164 from.statement_index - 1
167 }
168
169 Effect::Primary => {
170 let location = Location { block, statement_index: from.statement_index };
171 let statement = &block_data.statements[from.statement_index];
172
173 analysis.apply_primary_statement_effect(state, statement, location);
174 if to == Effect::Primary.at_index(from.statement_index) {
175 return;
176 }
177
178 from.statement_index - 1
179 }
180
181 Effect::Early => from.statement_index,
182 };
183
184 for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
187 let location = Location { block, statement_index };
188 let statement = &block_data.statements[statement_index];
189 analysis.apply_early_statement_effect(state, statement, location);
190 analysis.apply_primary_statement_effect(state, statement, location);
191 }
192
193 let location = Location { block, statement_index: to.statement_index };
196 let statement = &block_data.statements[to.statement_index];
197 analysis.apply_early_statement_effect(state, statement, location);
198
199 if to.effect == Effect::Early {
200 return;
201 }
202
203 analysis.apply_primary_statement_effect(state, statement, location);
204 }
205
206 fn visit_results_in_block<'mir, 'tcx, A>(
207 analysis: &A,
208 state: &mut A::Domain,
209 block: BasicBlock,
210 block_data: &'mir mir::BasicBlockData<'tcx>,
211 vis: &mut impl ResultsVisitor<'tcx, A>,
212 ) where
213 A: Analysis<'tcx>,
214 {
215 vis.visit_block_end(state);
216
217 let loc = Location { block, statement_index: block_data.statements.len() };
218 let term = block_data.terminator();
219 analysis.apply_early_terminator_effect(state, term, loc);
220 vis.visit_after_early_terminator_effect(analysis, state, term, loc);
221 analysis.apply_primary_terminator_effect(state, term, loc);
222 vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
223
224 for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
225 let loc = Location { block, statement_index };
226 analysis.apply_early_statement_effect(state, stmt, loc);
227 vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
228 analysis.apply_primary_statement_effect(state, stmt, loc);
229 vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
230 }
231
232 vis.visit_block_start(state);
233 }
234}
235
236pub struct Forward;
238
239impl Direction for Forward {
240 const IS_FORWARD: bool = true;
241
242 fn apply_effects_in_block<'mir, 'tcx, A>(
243 analysis: &A,
244 body: &mir::Body<'tcx>,
245 state: &mut A::Domain,
246 block: BasicBlock,
247 block_data: &'mir mir::BasicBlockData<'tcx>,
248 mut propagate: impl FnMut(BasicBlock, &A::Domain),
249 ) where
250 A: Analysis<'tcx>,
251 {
252 for (statement_index, statement) in block_data.statements.iter().enumerate() {
253 let location = Location { block, statement_index };
254 analysis.apply_early_statement_effect(state, statement, location);
255 analysis.apply_primary_statement_effect(state, statement, location);
256 }
257 let terminator = block_data.terminator();
258 let location = Location { block, statement_index: block_data.statements.len() };
259 analysis.apply_early_terminator_effect(state, terminator, location);
260 let edges = analysis.apply_primary_terminator_effect(state, terminator, location);
261
262 let exit_state = state;
263 match edges {
264 TerminatorEdges::None => {}
265 TerminatorEdges::Single(target) => propagate(target, exit_state),
266 TerminatorEdges::Double(target, unwind) => {
267 propagate(target, exit_state);
268 propagate(unwind, exit_state);
269 }
270 TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
271 if let Some(cleanup) = cleanup {
273 propagate(cleanup, exit_state);
274 }
275
276 if !return_.is_empty() {
277 analysis.apply_call_return_effect(exit_state, block, place);
278 for &target in return_ {
279 propagate(target, exit_state);
280 }
281 }
282 }
283 TerminatorEdges::SwitchInt { targets, discr } => {
284 if let Some(mut data) = analysis.get_switch_int_data(block, targets, discr) {
285 let mut tmp = analysis.bottom_value(body);
286 for (i, (_value, target)) in targets.iter().enumerate() {
287 tmp.clone_from(exit_state);
288 let target_idx = SwitchTargetIndex::Normal(i);
289 analysis.apply_switch_int_edge_effect(&mut tmp, &mut data, target_idx);
290 propagate(target, &tmp);
291 }
292
293 analysis.apply_switch_int_edge_effect(
297 exit_state,
298 &mut data,
299 SwitchTargetIndex::Otherwise,
300 );
301 propagate(targets.otherwise(), exit_state);
302 } else {
303 for target in targets.all_targets() {
304 propagate(*target, exit_state);
305 }
306 }
307 }
308 }
309 }
310
311 fn apply_effects_in_range<'tcx, A>(
312 analysis: &A,
313 state: &mut A::Domain,
314 block: BasicBlock,
315 block_data: &mir::BasicBlockData<'tcx>,
316 effects: RangeInclusive<EffectIndex>,
317 ) where
318 A: Analysis<'tcx>,
319 {
320 let (from, to) = (*effects.start(), *effects.end());
321 let terminator_index = block_data.statements.len();
322
323 if !(to.statement_index <= terminator_index) {
::core::panicking::panic("assertion failed: to.statement_index <= terminator_index")
};assert!(to.statement_index <= terminator_index);
324 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));
325
326 let first_unapplied_index = match from.effect {
330 Effect::Early => from.statement_index,
331
332 Effect::Primary if from.statement_index == terminator_index => {
333 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);
334
335 let location = Location { block, statement_index: terminator_index };
336 let terminator = block_data.terminator();
337 analysis.apply_primary_terminator_effect(state, terminator, location);
338 return;
339 }
340
341 Effect::Primary => {
342 let location = Location { block, statement_index: from.statement_index };
343 let statement = &block_data.statements[from.statement_index];
344 analysis.apply_primary_statement_effect(state, statement, location);
345
346 if from == to {
349 return;
350 }
351
352 from.statement_index + 1
353 }
354 };
355
356 for statement_index in first_unapplied_index..to.statement_index {
359 let location = Location { block, statement_index };
360 let statement = &block_data.statements[statement_index];
361 analysis.apply_early_statement_effect(state, statement, location);
362 analysis.apply_primary_statement_effect(state, statement, location);
363 }
364
365 let location = Location { block, statement_index: to.statement_index };
368 if to.statement_index == terminator_index {
369 let terminator = block_data.terminator();
370 analysis.apply_early_terminator_effect(state, terminator, location);
371
372 if to.effect == Effect::Primary {
373 analysis.apply_primary_terminator_effect(state, terminator, location);
374 }
375 } else {
376 let statement = &block_data.statements[to.statement_index];
377 analysis.apply_early_statement_effect(state, statement, location);
378
379 if to.effect == Effect::Primary {
380 analysis.apply_primary_statement_effect(state, statement, location);
381 }
382 }
383 }
384
385 fn visit_results_in_block<'mir, 'tcx, A>(
386 analysis: &A,
387 state: &mut A::Domain,
388 block: BasicBlock,
389 block_data: &'mir mir::BasicBlockData<'tcx>,
390 vis: &mut impl ResultsVisitor<'tcx, A>,
391 ) where
392 A: Analysis<'tcx>,
393 {
394 vis.visit_block_start(state);
395
396 for (statement_index, stmt) in block_data.statements.iter().enumerate() {
397 let loc = Location { block, statement_index };
398 analysis.apply_early_statement_effect(state, stmt, loc);
399 vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
400 analysis.apply_primary_statement_effect(state, stmt, loc);
401 vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
402 }
403
404 let loc = Location { block, statement_index: block_data.statements.len() };
405 let term = block_data.terminator();
406 analysis.apply_early_terminator_effect(state, term, loc);
407 vis.visit_after_early_terminator_effect(analysis, state, term, loc);
408 analysis.apply_primary_terminator_effect(state, term, loc);
409 vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
410
411 vis.visit_block_end(state);
412 }
413}