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