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}