rapx/analysis/core/alias_analysis/default/
mop.rs

1use rustc_hir::def_id::DefId;
2use rustc_middle::{
3    mir::{
4        Operand::{self, Constant, Copy, Move},
5        SwitchTargets, TerminatorKind,
6    },
7    ty::{TyKind, TypingEnv},
8};
9
10use std::collections::HashSet;
11
12use crate::analysis::graphs::scc::{SccInfo, SccTree};
13
14use super::{block::Term, graph::*, *};
15
16impl<'tcx> MopGraph<'tcx> {
17    pub fn split_check(
18        &mut self,
19        bb_idx: usize,
20        fn_map: &mut MopFnAliasMap,
21        recursion_set: &mut HashSet<DefId>,
22    ) {
23        /* duplicate the status before visiting a path; */
24        let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
25        let backup_constant = self.constants.clone();
26        let backup_alias_sets = self.alias_sets.clone();
27        self.check(bb_idx, fn_map, recursion_set);
28        /* restore after visit */
29        self.alias_sets = backup_alias_sets;
30        self.values = backup_values;
31        self.constants = backup_constant;
32    }
33    pub fn split_check_with_cond(
34        &mut self,
35        bb_idx: usize,
36        path_discr_id: usize,
37        path_discr_val: usize,
38        fn_map: &mut MopFnAliasMap,
39        recursion_set: &mut HashSet<DefId>,
40    ) {
41        /* duplicate the status before visiting a path; */
42        let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
43        let backup_constant = self.constants.clone();
44        let backup_alias_sets = self.alias_sets.clone();
45        /* add control-sensitive indicator to the path status */
46        self.constants.insert(path_discr_id, path_discr_val);
47        self.check(bb_idx, fn_map, recursion_set);
48        /* restore after visit */
49        self.alias_sets = backup_alias_sets;
50        self.values = backup_values;
51        self.constants = backup_constant;
52    }
53
54    // the core function of the alias analysis algorithm.
55    pub fn check(
56        &mut self,
57        bb_idx: usize,
58        fn_map: &mut MopFnAliasMap,
59        recursion_set: &mut HashSet<DefId>,
60    ) {
61        self.visit_times += 1;
62        if self.visit_times > VISIT_LIMIT {
63            return;
64        }
65        let scc_idx = self.blocks[bb_idx].scc.enter;
66        let cur_block = self.blocks[bb_idx].clone();
67
68        rap_debug!("check {:?}", bb_idx);
69        if bb_idx == scc_idx && !cur_block.scc.nodes.is_empty() {
70            rap_debug!("check {:?} as a scc", bb_idx);
71            self.check_scc(bb_idx, fn_map, recursion_set);
72        } else {
73            self.check_single_node(bb_idx, fn_map, recursion_set);
74            self.handle_nexts(bb_idx, fn_map, None, recursion_set);
75        }
76    }
77
78    pub fn check_scc(
79        &mut self,
80        bb_idx: usize,
81        fn_map: &mut MopFnAliasMap,
82        recursion_set: &mut HashSet<DefId>,
83    ) {
84        let cur_block = self.blocks[bb_idx].clone();
85
86        /* Handle cases if the current block is a merged scc block with sub block */
87        rap_debug!("Searchng paths in scc: {:?}, {:?}", bb_idx, cur_block.scc);
88        let scc_tree = self.sort_scc_tree(&cur_block.scc);
89        rap_debug!("scc_tree: {:?}", scc_tree);
90        let paths_in_scc = self.find_scc_paths(bb_idx, &scc_tree, &mut FxHashMap::default());
91        rap_debug!("Paths found in scc: {:?}", paths_in_scc);
92
93        let backup_values = self.values.clone(); // duplicate the status when visiteding different paths;
94        let backup_constant = self.constants.clone();
95        let backup_alias_sets = self.alias_sets.clone();
96        let backup_recursion_set = recursion_set.clone();
97        for raw_path in paths_in_scc {
98            self.alias_sets = backup_alias_sets.clone();
99            self.values = backup_values.clone();
100            self.constants = backup_constant.clone();
101            *recursion_set = backup_recursion_set.clone();
102
103            let path = raw_path.0;
104            let path_constraints = &raw_path.1;
105            rap_debug!("checking path: {:?}", path);
106            if !path.is_empty() {
107                for idx in &path[..path.len() - 1] {
108                    self.alias_bb(*idx);
109                    self.alias_bbcall(*idx, fn_map, recursion_set);
110                }
111            }
112            // The last node is already ouside the scc.
113            if let Some(&last_node) = path.last() {
114                if self.blocks[last_node].scc.nodes.is_empty() {
115                    self.check_single_node(last_node, fn_map, recursion_set);
116                    self.handle_nexts(last_node, fn_map, Some(path_constraints), recursion_set);
117                } else {
118                    // If the exit is an scc, we should handle it like check_scc;
119                }
120            }
121        }
122    }
123
124    pub fn check_single_node(
125        &mut self,
126        bb_idx: usize,
127        fn_map: &mut MopFnAliasMap,
128        recursion_set: &mut HashSet<DefId>,
129    ) {
130        rap_debug!("check {:?} as a node", bb_idx);
131        let cur_block = self.blocks[bb_idx].clone();
132        self.alias_bb(self.blocks[bb_idx].scc.enter);
133        self.alias_bbcall(self.blocks[bb_idx].scc.enter, fn_map, recursion_set);
134        if cur_block.next.is_empty() {
135            self.merge_results();
136            return;
137        }
138    }
139
140    pub fn handle_nexts(
141        &mut self,
142        bb_idx: usize,
143        fn_map: &mut MopFnAliasMap,
144        path_constraints: Option<&FxHashMap<usize, usize>>,
145        recursion_set: &mut HashSet<DefId>,
146    ) {
147        let cur_block = self.blocks[bb_idx].clone();
148        let tcx = self.tcx;
149
150        rap_debug!(
151            "handle nexts {:?} of node {:?}",
152            self.blocks[bb_idx].next,
153            bb_idx
154        );
155        // Extra path contraints are introduced during scc handling.
156        if let Some(path_constraints) = path_constraints {
157            self.constants.extend(path_constraints);
158        }
159        /* Begin: handle the SwitchInt statement. */
160        let mut single_target = false;
161        let mut sw_val = 0;
162        let mut sw_target = 0; // Single target
163        let mut path_discr_id = 0; // To avoid analyzing paths that cannot be reached with one enum type.
164        let mut sw_targets = None; // Multiple targets of SwitchInt
165
166        match cur_block.terminator {
167            Term::Switch(switch) => {
168                if let TerminatorKind::SwitchInt {
169                    ref discr,
170                    ref targets,
171                } = switch.kind
172                {
173                    match discr {
174                        Copy(p) | Move(p) => {
175                            let value_idx = self.projection(*p);
176                            let local_decls = &tcx.optimized_mir(self.def_id).local_decls;
177                            let place_ty = (*p).ty(local_decls, tcx);
178                            rap_debug!("value_idx: {:?}", value_idx);
179                            match place_ty.ty.kind() {
180                                TyKind::Bool => {
181                                    if let Some(constant) = self.constants.get(&value_idx) {
182                                        if *constant != usize::MAX {
183                                            single_target = true;
184                                            sw_val = *constant;
185                                        }
186                                    }
187                                    path_discr_id = value_idx;
188                                    sw_targets = Some(targets.clone());
189                                }
190                                _ => {
191                                    if let Some(father) =
192                                        self.discriminants.get(&self.values[value_idx].local)
193                                    {
194                                        if let Some(constant) = self.constants.get(father) {
195                                            if *constant != usize::MAX {
196                                                single_target = true;
197                                                sw_val = *constant;
198                                            }
199                                        }
200                                        if self.values[value_idx].local == value_idx {
201                                            path_discr_id = *father;
202                                            sw_targets = Some(targets.clone());
203                                        }
204                                    }
205                                }
206                            }
207                        }
208                        Constant(c) => {
209                            single_target = true;
210                            let ty_env = TypingEnv::post_analysis(tcx, self.def_id);
211                            if let Some(val) = c.const_.try_eval_target_usize(tcx, ty_env) {
212                                sw_val = val as usize;
213                            }
214                        }
215                    }
216                    if single_target {
217                        /* Find the target based on the value;
218                         * Since sw_val is a const, only one target is reachable.
219                         * Filed 0 is the value; field 1 is the real target.
220                         */
221                        rap_debug!("targets: {:?}; sw_val = {:?}", targets, sw_val);
222                        for iter in targets.iter() {
223                            if iter.0 as usize == sw_val {
224                                sw_target = iter.1.as_usize();
225                                break;
226                            }
227                        }
228                        /* No target found, choose the default target.
229                         * The default targets is not included within the iterator.
230                         * We can only obtain the default target based on the last item of all_targets().
231                         */
232                        if sw_target == 0 {
233                            let all_target = targets.all_targets();
234                            sw_target = all_target[all_target.len() - 1].as_usize();
235                        }
236                    }
237                }
238            }
239            _ => {
240                // Not SwitchInt
241            }
242        }
243        /* End: finish handling SwitchInt */
244        // fixed path since a constant switchInt value
245        if single_target {
246            rap_debug!("visit a single target: {:?}", sw_target);
247            self.check(sw_target, fn_map, recursion_set);
248        } else {
249            // Other cases in switchInt terminators
250            if let Some(targets) = sw_targets {
251                for iter in targets.iter() {
252                    if self.visit_times > VISIT_LIMIT {
253                        continue;
254                    }
255                    let next = iter.1.as_usize();
256                    let path_discr_val = iter.0 as usize;
257                    self.split_check_with_cond(
258                        next,
259                        path_discr_id,
260                        path_discr_val,
261                        fn_map,
262                        recursion_set,
263                    );
264                }
265                let all_targets = targets.all_targets();
266                let next_index = all_targets[all_targets.len() - 1].as_usize();
267                let path_discr_val = usize::MAX; // to indicate the default path;
268                self.split_check_with_cond(
269                    next_index,
270                    path_discr_id,
271                    path_discr_val,
272                    fn_map,
273                    recursion_set,
274                );
275            } else {
276                for next in cur_block.next {
277                    if self.visit_times > VISIT_LIMIT {
278                        continue;
279                    }
280                    self.split_check(next, fn_map, recursion_set);
281                }
282            }
283        }
284    }
285
286    pub fn sort_scc_tree(&mut self, scc: &SccInfo) -> SccTree {
287        // child_enter -> SccInfo
288        let mut child_sccs: FxHashMap<usize, SccInfo> = FxHashMap::default();
289
290        // find all sub sccs
291        for &node in scc.nodes.iter() {
292            let node_scc = &self.blocks[node].scc;
293            if node_scc.enter != scc.enter && !node_scc.nodes.is_empty() {
294                child_sccs
295                    .entry(node_scc.enter)
296                    .or_insert_with(|| node_scc.clone());
297            }
298        }
299
300        // recursively sort children
301        let children = child_sccs
302            .into_values()
303            .map(|child_scc| self.sort_scc_tree(&child_scc))
304            .collect();
305
306        SccTree {
307            scc: scc.clone(),
308            children,
309        }
310    }
311
312    pub fn find_scc_paths(
313        &mut self,
314        start: usize,
315        scc_tree: &SccTree,
316        path_constraints: &mut FxHashMap<usize, usize>,
317    ) -> Vec<(Vec<usize>, FxHashMap<usize, usize>)> {
318        let mut all_paths = Vec::new();
319        let mut scc_path_set: HashSet<Vec<usize>> = HashSet::new();
320
321        self.find_scc_paths_inner(
322            start,
323            start,
324            scc_tree,
325            &mut vec![start],
326            path_constraints,
327            &mut all_paths,
328            &mut scc_path_set,
329        );
330
331        all_paths
332    }
333
334    fn find_scc_paths_inner(
335        &mut self,
336        start: usize,
337        cur: usize,
338        scc_tree: &SccTree,
339        path: &mut Vec<usize>,
340        path_constraints: &mut FxHashMap<usize, usize>,
341        paths_in_scc: &mut Vec<(Vec<usize>, FxHashMap<usize, usize>)>,
342        scc_path_set: &mut HashSet<Vec<usize>>,
343    ) {
344        rap_debug!("cur = {}", cur);
345        let scc = &scc_tree.scc;
346        if scc.nodes.is_empty() {
347            paths_in_scc.push((path.clone(), path_constraints.clone()));
348            return;
349        }
350        // FIX ME: a temp complexity control;
351        if path.len() > 100 || paths_in_scc.len() > 100 {
352            return;
353        }
354        if !scc.nodes.contains(&cur) && start != cur {
355            rap_debug!("new path: {:?}", path.clone());
356            // we add the next node into the scc path to speedup the traveral outside
357            // the scc.
358            paths_in_scc.push((path.clone(), path_constraints.clone()));
359            return;
360        }
361
362        if cur == start && path.len() > 1 {
363            let last_index = path[..path.len() - 1]
364                .iter()
365                .rposition(|&node| node == start)
366                .map(|i| i)
367                .unwrap_or(0);
368            let slice = &path[last_index..];
369            rap_debug!("path: {:?}", path);
370            rap_debug!("slice: {:?}", slice);
371            rap_debug!("set: {:?}", scc_path_set);
372            if scc_path_set.contains(slice) {
373                return;
374            } else {
375                scc_path_set.insert(slice.to_vec());
376            }
377        }
378
379        // Clear the constriants if the local is reassigned in the current block;
380        // Otherwise, it cannot reach other branches.
381        for local in &self.blocks[cur].assigned_locals {
382            rap_debug!(
383                "Remove path_constraints {:?}, because it has been reassigned.",
384                local
385            );
386            path_constraints.remove(&local);
387        }
388
389        // Find the pathes of inner scc recursively;
390        for child_tree in &scc_tree.children {
391            let child_enter = child_tree.scc.enter;
392            if cur == child_enter {
393                let sub_paths = self.find_scc_paths(child_enter, child_tree, path_constraints);
394                rap_debug!("paths in sub scc: {}, {:?}", child_enter, sub_paths);
395                for (subp, subconst) in sub_paths {
396                    let mut new_path = path.clone();
397                    new_path.extend(&subp[1..]);
398                    let mut new_const = path_constraints.clone();
399                    new_const.extend(subconst.iter());
400                    self.find_scc_paths_inner(
401                        start,
402                        *new_path.last().unwrap(),
403                        scc_tree,
404                        &mut new_path,
405                        &mut new_const,
406                        paths_in_scc,
407                        scc_path_set,
408                    );
409                }
410                return;
411            }
412        }
413
414        let term = &self.terminators[cur].clone();
415
416        rap_debug!("term: {:?}", term);
417        match term {
418            TerminatorKind::SwitchInt { discr, targets } => {
419                self.handle_switch_int_case(
420                    start,
421                    cur,
422                    path,
423                    scc_tree,
424                    path_constraints,
425                    paths_in_scc,
426                    discr,
427                    targets,
428                    scc_path_set,
429                );
430            }
431            _ => {
432                for next in self.blocks[cur].next.clone() {
433                    path.push(next);
434                    self.find_scc_paths_inner(
435                        start,
436                        next,
437                        scc_tree,
438                        path,
439                        path_constraints,
440                        paths_in_scc,
441                        scc_path_set,
442                    );
443                    rap_debug!("pop 1 : {:?}", path.last());
444                    path.pop();
445                }
446            }
447        }
448    }
449
450    fn handle_switch_int_case(
451        &mut self,
452        start: usize,
453        _cur: usize,
454        path: &mut Vec<usize>,
455        scc_tree: &SccTree,
456        path_constraints: &mut FxHashMap<usize, usize>,
457        paths_in_scc: &mut Vec<(Vec<usize>, FxHashMap<usize, usize>)>,
458        discr: &Operand<'tcx>,
459        targets: &SwitchTargets,
460        scc_path_set: &mut std::collections::HashSet<Vec<usize>>,
461    ) {
462        let place = match discr {
463            Copy(p) | Move(p) => Some(self.projection(*p)),
464            _ => None,
465        };
466
467        if let Some(place) = place {
468            let discr_local = self
469                .discriminants
470                .get(&self.values[place].local)
471                .cloned()
472                .unwrap_or(place);
473
474            rap_debug!(
475                "Handle SwitchInt, discr = {:?}, local = {:?}",
476                discr,
477                discr_local
478            );
479            if let Some(&constant) = path_constraints.get(&discr_local) {
480                rap_debug!("constant = {:?}", constant);
481                rap_debug!("targets = {:?}", targets);
482                let mut otherwise = true;
483                // Only the branch matching constant
484                targets
485                    .iter()
486                    .filter(|t| t.0 as usize == constant)
487                    .for_each(|branch| {
488                        let target = branch.1.as_usize();
489                        path.push(target);
490                        self.find_scc_paths_inner(
491                            start,
492                            target,
493                            scc_tree,
494                            path,
495                            path_constraints,
496                            paths_in_scc,
497                            scc_path_set,
498                        );
499                        rap_debug!("pop 2: {:?}", path.last());
500                        path.pop();
501                        otherwise = false;
502                    });
503                if otherwise {
504                    let target = targets.otherwise().as_usize();
505                    path.push(target);
506                    path_constraints.insert(discr_local, targets.iter().len());
507                    self.find_scc_paths_inner(
508                        start,
509                        target,
510                        scc_tree,
511                        path,
512                        path_constraints,
513                        paths_in_scc,
514                        scc_path_set,
515                    );
516                    path_constraints.remove(&discr_local);
517                    rap_debug!("pop 3: {:?}", path.last());
518                    path.pop();
519                }
520            } else {
521                // No restriction, try each branch
522                for branch in targets.iter() {
523                    let constant = branch.0 as usize;
524                    let target = branch.1.as_usize();
525                    path.push(target);
526                    path_constraints.insert(discr_local, constant);
527                    self.find_scc_paths_inner(
528                        start,
529                        target,
530                        scc_tree,
531                        path,
532                        path_constraints,
533                        paths_in_scc,
534                        scc_path_set,
535                    );
536                    path_constraints.remove(&discr_local);
537                    rap_debug!("pop 4: {:?}", path.last());
538                    path.pop();
539                }
540                // Handle default branch
541                let target = targets.otherwise().as_usize();
542                path.push(target);
543                path_constraints.insert(discr_local, targets.iter().len());
544                self.find_scc_paths_inner(
545                    start,
546                    target,
547                    scc_tree,
548                    path,
549                    path_constraints,
550                    paths_in_scc,
551                    scc_path_set,
552                );
553                path_constraints.remove(&discr_local);
554                rap_debug!("pop 5: {:?}", path.last());
555                path.pop();
556            }
557        }
558    }
559}