rapx/analysis/core/alias_analysis/default/mop.rs
1use rustc_hir::def_id::DefId;
2use rustc_middle::{
3 mir::{
4 Operand::{Constant, Copy, Move},
5 TerminatorKind,
6 },
7 ty::{TyKind, TypingEnv},
8};
9
10use std::collections::{HashMap, HashSet};
11
12use super::{block::Term, graph::*, *};
13
14impl<'tcx> MopGraph<'tcx> {
15 pub fn split_check(
16 &mut self,
17 bb_idx: usize,
18 fn_map: &mut MopAAResultMap,
19 recursion_set: &mut HashSet<DefId>,
20 ) {
21 /* duplicate the status before visiting a path; */
22 let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
23 let backup_constant = self.constants.clone();
24 let backup_alias_set = self.alias_set.clone();
25 self.check(bb_idx, fn_map, recursion_set);
26 /* restore after visit */
27 self.alias_set = backup_alias_set;
28 self.values = backup_values;
29 self.constants = backup_constant;
30 }
31 pub fn split_check_with_cond(
32 &mut self,
33 bb_idx: usize,
34 path_discr_id: usize,
35 path_discr_val: usize,
36 fn_map: &mut MopAAResultMap,
37 recursion_set: &mut HashSet<DefId>,
38 ) {
39 /* duplicate the status before visiting a path; */
40 let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
41 let backup_constant = self.constants.clone();
42 let backup_alias_set = self.alias_set.clone();
43 /* add control-sensitive indicator to the path status */
44 self.constants.insert(path_discr_id, path_discr_val);
45 self.check(bb_idx, fn_map, recursion_set);
46 /* restore after visit */
47 self.alias_set = backup_alias_set;
48 self.values = backup_values;
49 self.constants = backup_constant;
50 }
51
52 // the core function of the alias analysis algorithm.
53 pub fn check(
54 &mut self,
55 bb_idx: usize,
56 fn_map: &mut MopAAResultMap,
57 recursion_set: &mut HashSet<DefId>,
58 ) {
59 self.visit_times += 1;
60 if self.visit_times > VISIT_LIMIT {
61 return;
62 }
63 let scc_idx = self.blocks[bb_idx].scc.enter;
64 let cur_block = self.blocks[bb_idx].clone();
65 self.alias_bb(self.blocks[bb_idx].scc.enter);
66 self.alias_bbcall(self.blocks[bb_idx].scc.enter, fn_map, recursion_set);
67
68 if bb_idx == scc_idx {
69 let mut paths_in_scc = vec![];
70
71 /* Handle cases if the current block is a merged scc block with sub block */
72 self.calculate_scc_order(
73 scc_idx,
74 bb_idx,
75 &mut cur_block.scc.clone().nodes,
76 &mut vec![],
77 &mut HashMap::new(),
78 &mut HashSet::new(),
79 &mut paths_in_scc,
80 );
81
82 let backup_values = self.values.clone(); // duplicate the status when visiteding different paths;
83 let backup_constant = self.constants.clone();
84 let backup_alias_set = self.alias_set.clone();
85 let backup_fn_map = fn_map.clone();
86 let backup_recursion_set = recursion_set.clone();
87 for each_path in paths_in_scc {
88 self.alias_set = backup_alias_set.clone();
89 self.values = backup_values.clone();
90 self.constants = backup_constant.clone();
91 *fn_map = backup_fn_map.clone();
92 *recursion_set = backup_recursion_set.clone();
93
94 if !each_path.is_empty() {
95 for idx in each_path {
96 self.alias_bb(idx);
97 self.alias_bbcall(idx, fn_map, recursion_set);
98 }
99 }
100 }
101
102 match cur_block.next.len() {
103 0 => {
104 let results_nodes = self.values.clone();
105 self.merge_results(results_nodes);
106 return;
107 }
108 1 => {
109 /*
110 * Equivalent to self.check(cur_block.next[0]..);
111 * We cannot use [0] for FxHashSet.
112 */
113 for next in cur_block.next {
114 self.check(next, fn_map, recursion_set);
115 }
116 return;
117 }
118 _ => {}
119 }
120 }
121
122 /* Begin: handle the SwitchInt statement. */
123 let mut single_target = false;
124 let mut sw_val = 0;
125 let mut sw_target = 0; // Single target
126 let mut path_discr_id = 0; // To avoid analyzing paths that cannot be reached with one enum type.
127 let mut sw_targets = None; // Multiple targets of SwitchInt
128
129 if let Term::Switch(switch) = cur_block.terminator
130 && cur_block.scc.nodes.is_empty()
131 {
132 if let TerminatorKind::SwitchInt {
133 ref discr,
134 ref targets,
135 } = switch.kind
136 {
137 match discr {
138 Copy(p) | Move(p) => {
139 let place = self.projection(false, *p);
140 let local_decls = &self.tcx.optimized_mir(self.def_id).local_decls;
141 let place_ty = (*p).ty(local_decls, self.tcx);
142 rap_debug!("place {:?}", place);
143 match place_ty.ty.kind() {
144 TyKind::Bool => {
145 if let Some(constant) = self.constants.get(&place) {
146 if *constant != usize::MAX {
147 single_target = true;
148 sw_val = *constant;
149 }
150 }
151 path_discr_id = place;
152 sw_targets = Some(targets.clone());
153 }
154 _ => {
155 if let Some(father) =
156 self.discriminants.get(&self.values[place].local)
157 {
158 if let Some(constant) = self.constants.get(father) {
159 if *constant != usize::MAX {
160 single_target = true;
161 sw_val = *constant;
162 }
163 }
164 if self.values[place].local == place {
165 path_discr_id = *father;
166 sw_targets = Some(targets.clone());
167 }
168 }
169 }
170 }
171 }
172 Constant(c) => {
173 single_target = true;
174 let ty_env = TypingEnv::post_analysis(self.tcx, self.def_id);
175 if let Some(val) = c.const_.try_eval_target_usize(self.tcx, ty_env) {
176 sw_val = val as usize;
177 }
178 }
179 }
180 if single_target {
181 /* Find the target based on the value;
182 * Since sw_val is a const, only one target is reachable.
183 * Filed 0 is the value; field 1 is the real target.
184 */
185 for iter in targets.iter() {
186 if iter.0 as usize == sw_val {
187 sw_target = iter.1.as_usize();
188 break;
189 }
190 }
191 /* No target found, choose the default target.
192 * The default targets is not included within the iterator.
193 * We can only obtain the default target based on the last item of all_targets().
194 */
195 if sw_target == 0 {
196 let all_target = targets.all_targets();
197 sw_target = all_target[all_target.len() - 1].as_usize();
198 }
199 }
200 }
201 }
202 /* End: finish handling SwitchInt */
203 // fixed path since a constant switchInt value
204 if single_target {
205 self.check(sw_target, fn_map, recursion_set);
206 } else {
207 // Other cases in switchInt terminators
208 if let Some(targets) = sw_targets {
209 for iter in targets.iter() {
210 if self.visit_times > VISIT_LIMIT {
211 continue;
212 }
213 let next_index = iter.1.as_usize();
214 let path_discr_val = iter.0 as usize;
215 self.split_check_with_cond(
216 next_index,
217 path_discr_id,
218 path_discr_val,
219 fn_map,
220 recursion_set,
221 );
222 }
223 let all_targets = targets.all_targets();
224 let next_index = all_targets[all_targets.len() - 1].as_usize();
225 let path_discr_val = usize::MAX; // to indicate the default path;
226 self.split_check_with_cond(
227 next_index,
228 path_discr_id,
229 path_discr_val,
230 fn_map,
231 recursion_set,
232 );
233 } else {
234 for i in cur_block.next {
235 if self.visit_times > VISIT_LIMIT {
236 continue;
237 }
238 let next_index = i;
239 self.split_check(next_index, fn_map, recursion_set);
240 }
241 }
242 }
243 }
244
245 ///This function performs a DFS traversal across the SCC, extracting all possible orderings
246 /// that respect the control-flow structure and SwitchInt branching, taking into account
247 /// enum discriminants and constant branches.
248 pub fn calculate_scc_order(
249 &mut self,
250 start: usize,
251 cur: usize,
252 scc: &Vec<usize>,
253 path: &mut Vec<usize>,
254 stacked_discriminants: &mut HashMap<usize, usize>,
255 visited: &mut HashSet<usize>, // for cycle detection.
256 paths_in_scc: &mut Vec<Vec<usize>>,
257 ) {
258 // If we have returned to the start and the path is non-empty, we've found a cycle/path.
259 if cur == start && !path.is_empty() {
260 paths_in_scc.push(path.clone());
261 return;
262 }
263 // Mark the current block as visited in this path to avoid cycles in this DFS.
264 visited.insert(cur);
265 // Retrieve the terminator for this block (the outgoing control flow).
266 let term = &self.terminators[cur].clone();
267
268 match term {
269 TerminatorKind::SwitchInt { discr, targets } => {
270 match discr {
271 // Case 1: The discriminant is a place (value held in memory, e.g., enum field)
272 Copy(p) | Move(p) => {
273 let place = self.projection(false, *p);
274 if let Some(real_discr_local) =
275 self.discriminants.get(&self.values[place].local)
276 {
277 let real_discr_local = *real_discr_local;
278 // There are already restrictions related to the discriminant;
279 // Only the branch that meets the restriction can be taken.
280 if let Some(constant) = stacked_discriminants.get(&real_discr_local) {
281 let constant = *constant;
282 for branch in targets.iter() {
283 // branch is a tupele (value, target)
284 if branch.0 as usize == constant {
285 let target = branch.1.as_usize();
286 if path.contains(&target) {
287 continue;
288 }
289 path.push(target);
290 self.calculate_scc_order(
291 start,
292 target,
293 scc,
294 path,
295 stacked_discriminants,
296 visited,
297 paths_in_scc,
298 );
299 path.pop();
300 }
301 }
302 } else {
303 // No restrictions yet;
304 // Visit each branch with new condition add to the
305 // stacked_discriminants.
306 for branch in targets.iter() {
307 let constant = branch.0 as usize;
308 let target = branch.1.as_usize();
309 if path.contains(&target) {
310 continue;
311 }
312 path.push(target);
313 stacked_discriminants.insert(real_discr_local, constant);
314 self.calculate_scc_order(
315 start,
316 target,
317 scc,
318 path,
319 stacked_discriminants,
320 visited,
321 paths_in_scc,
322 );
323 stacked_discriminants.remove(&real_discr_local);
324 path.pop();
325 }
326 }
327 } else {
328 if let Some(constant) = stacked_discriminants.get(&place) {
329 let constant = *constant;
330 for t in targets.iter() {
331 if t.0 as usize == constant {
332 let target = t.1.as_usize();
333 if path.contains(&target) {
334 continue;
335 }
336 path.push(target);
337 self.calculate_scc_order(
338 start,
339 target,
340 scc,
341 path,
342 stacked_discriminants,
343 visited,
344 paths_in_scc,
345 );
346 path.pop();
347 }
348 }
349 } else {
350 for t in targets.iter() {
351 let constant = t.0 as usize;
352 let target = t.1.as_usize();
353 if path.contains(&target) {
354 continue;
355 }
356 path.push(target);
357 stacked_discriminants.insert(place, constant);
358 self.calculate_scc_order(
359 start,
360 target,
361 scc,
362 path,
363 stacked_discriminants,
364 visited,
365 paths_in_scc,
366 );
367 stacked_discriminants.remove(&place);
368 path.pop();
369 }
370
371 let constant = targets.iter().len();
372 let target = targets.otherwise().as_usize();
373 if !path.contains(&target) {
374 path.push(target);
375 stacked_discriminants.insert(place, constant);
376 self.calculate_scc_order(
377 start,
378 target,
379 scc,
380 path,
381 stacked_discriminants,
382 visited,
383 paths_in_scc,
384 );
385 stacked_discriminants.remove(&place);
386 path.pop();
387 }
388 }
389 }
390 }
391 _ => {}
392 }
393 }
394 _ => {
395 for next in self.blocks[cur].next.clone() {
396 if !scc.contains(&next) && next != start {
397 continue;
398 }
399 if next != start {
400 path.push(next);
401 self.calculate_scc_order(
402 start,
403 next,
404 scc,
405 path,
406 stacked_discriminants,
407 visited,
408 paths_in_scc,
409 );
410 path.pop();
411 } else {
412 self.calculate_scc_order(
413 start,
414 next,
415 scc,
416 path,
417 stacked_discriminants,
418 visited,
419 paths_in_scc,
420 );
421 }
422 }
423 }
424 }
425
426 visited.remove(&cur);
427 }
428}