rapx/analysis/alias_analysis/default/
mop.rs

1use rustc_hir::def_id::DefId;
2
3use std::collections::HashSet;
4
5use crate::analysis::path_analysis::{PathNode, PathTree};
6use crate::compat::{FxHashMap, FxHashSet};
7
8use super::value::Value;
9use super::{graph::*, *};
10
11#[derive(Clone)]
12struct MopStateSnapshot {
13    values: Vec<Value>,
14    constants: FxHashMap<usize, usize>,
15    alias_sets: Vec<FxHashSet<usize>>,
16}
17
18impl<'tcx> AliasGraph<'tcx> {
19    fn snapshot_state(&self) -> MopStateSnapshot {
20        MopStateSnapshot {
21            values: self.values.clone(),
22            constants: self.constants.clone(),
23            alias_sets: self.alias_sets.clone(),
24        }
25    }
26
27    fn restore_state(&mut self, snapshot: &MopStateSnapshot) {
28        self.values = snapshot.values.clone();
29        self.constants = snapshot.constants.clone();
30        self.alias_sets = snapshot.alias_sets.clone();
31    }
32
33    /// Process pre-enumerated whole-function paths via DFS on the path tree.
34    ///
35    /// Shared prefixes are processed once. State is saved at branch points
36    /// and restored before processing sibling subtrees, avoiding redundant
37    /// re-analysis of common path prefixes.
38    ///
39    /// If `precomputed_paths` is `Some`, it is used directly instead of
40    /// re-enumerating from the internal `PathGraph`. This allows callers
41    /// that have already cached a `PathTree` (e.g., via `PathAnalyzer`) to
42    /// avoid redundant work.
43    pub fn process_function_paths(
44        &mut self,
45        fn_map: &mut MopFnAliasMap,
46        recursion_set: &mut HashSet<DefId>,
47    ) {
48        self.process_function_paths_opt(None, fn_map, recursion_set)
49    }
50
51    /// Like `process_function_paths` but accepts an optional precomputed `PathTree`.
52    pub fn process_function_paths_opt(
53        &mut self,
54        precomputed_paths: Option<PathTree>,
55        fn_map: &mut MopFnAliasMap,
56        recursion_set: &mut HashSet<DefId>,
57    ) {
58        rap_debug!(
59            "process_function_paths: def_id={:?} blocks={}",
60            self.def_id(),
61            self.path_graph.cfg.blocks.len()
62        );
63        let paths = precomputed_paths.unwrap_or_else(|| self.enumerate_paths());
64        rap_debug!(
65            "process_function_paths: def_id={:?} paths_enumerated={}",
66            self.def_id(),
67            paths.len()
68        );
69
70        let Some(root) = paths.root() else {
71            return;
72        };
73        let mut path = Vec::new();
74        let _ = self.dfs_mop(root, &mut path, fn_map, recursion_set);
75    }
76
77    fn dfs_mop(
78        &mut self,
79        node: &PathNode,
80        path: &mut Vec<usize>,
81        fn_map: &mut MopFnAliasMap,
82        recursion_set: &mut HashSet<DefId>,
83    ) -> Result<(), ()> {
84        path.push(node.block);
85        self.alias_bb(node.block);
86        self.alias_bbcall(node.block, fn_map, recursion_set);
87
88        let saved_state = self.snapshot_state();
89        let saved_recursion = recursion_set.clone();
90
91        if node.is_path_end {
92            self.increment_visit_times();
93            if self.visit_times() > VISIT_LIMIT {
94                path.pop();
95                return Err(());
96            }
97            self.merge_results();
98        }
99
100        for child in &node.children {
101            self.restore_state(&saved_state);
102            *recursion_set = saved_recursion.clone();
103            self.dfs_mop(child, path, fn_map, recursion_set)?;
104        }
105
106        path.pop();
107        Ok(())
108    }
109}