rapx/analysis/core/range_analysis/
default.rs1#![allow(unused_imports)]
2
3use crate::{
4 analysis::{
5 Analysis,
6 core::{
7 alias_analysis::default::graph::MopGraph,
8 callgraph::{default::CallGraphInfo, visitor::CallGraphVisitor},
9 range_analysis::{
10 Range, RangeAnalysis,
11 domain::{
12 ConstraintGraph::ConstraintGraph,
13 domain::{ConstConvert, IntervalArithmetic, VarNodes},
14 },
15 },
16 ssa_transform::*,
17 },
18 graphs::scc::Scc,
19 },
20 rap_debug, rap_info,
21};
22
23use rustc_data_structures::fx::FxHashMap;
24use rustc_hir::{def::DefKind, def_id::DefId};
25use rustc_middle::{
26 mir::{Body, Place},
27 ty::TyCtxt,
28};
29use std::{
30 cell::RefCell,
31 collections::{HashMap, HashSet},
32 fmt::Debug,
33 fs::{self, File},
34 io::Write,
35 path::PathBuf,
36 rc::Rc,
37};
38
39use super::{PathConstraint, PathConstraintMap, RAResult, RAResultMap, RAVecResultMap};
40pub struct RangeAnalyzer<'tcx, T: IntervalArithmetic + ConstConvert + Debug> {
41 pub tcx: TyCtxt<'tcx>,
42 pub debug: bool,
43 pub ssa_def_id: Option<DefId>,
44 pub essa_def_id: Option<DefId>,
45 pub final_vars: RAResultMap<'tcx, T>,
46 pub ssa_places_mapping: FxHashMap<DefId, HashMap<Place<'tcx>, HashSet<Place<'tcx>>>>,
47 pub fn_constraintgraph_mapping: FxHashMap<DefId, ConstraintGraph<'tcx, T>>,
48 pub callgraph: CallGraphInfo<'tcx>,
49 pub body_map: FxHashMap<DefId, Body<'tcx>>,
50 pub cg_map: FxHashMap<DefId, Rc<RefCell<ConstraintGraph<'tcx, T>>>>,
51 pub vars_map: FxHashMap<DefId, Vec<RefCell<VarNodes<'tcx, T>>>>,
52 pub final_vars_vec: RAVecResultMap<'tcx, T>,
53 pub path_constraints: PathConstraintMap<'tcx>,
54}
55impl<'tcx, T: IntervalArithmetic + ConstConvert + Debug> Analysis for RangeAnalyzer<'tcx, T>
56where
57 T: IntervalArithmetic + ConstConvert + Debug,
58{
59 fn name(&self) -> &'static str {
60 "Range Analysis"
61 }
62
63 fn run(&mut self) {
64 self.only_caller_range_analysis();
66 self.start_path_constraints_analysis();
67 }
68
69 fn reset(&mut self) {
70 self.final_vars.clear();
71 self.ssa_places_mapping.clear();
72 }
73}
74
75impl<'tcx, T: IntervalArithmetic + ConstConvert + Debug> RangeAnalysis<'tcx, T>
76 for RangeAnalyzer<'tcx, T>
77where
78 T: IntervalArithmetic + ConstConvert + Debug,
79{
80 fn get_fn_range(&self, def_id: DefId) -> Option<RAResult<'tcx, T>> {
81 self.final_vars.get(&def_id).cloned()
82 }
83 fn get_fn_ranges_percall(&self, def_id: DefId) -> Option<Vec<RAResult<'tcx, T>>> {
84 self.final_vars_vec.get(&def_id).cloned()
85 }
86 fn get_all_fn_ranges(&self) -> RAResultMap<'tcx, T> {
87 self.final_vars.clone()
89 }
90 fn get_all_fn_ranges_percall(&self) -> RAVecResultMap<'tcx, T> {
91 self.final_vars_vec.clone()
92 }
93
94 fn get_fn_local_range(&self, def_id: DefId, place: Place<'tcx>) -> Option<Range<T>> {
96 self.final_vars
97 .get(&def_id)
98 .and_then(|vars| vars.get(&place).cloned())
99 }
100
101 fn get_fn_path_constraints(&self, def_id: DefId) -> Option<PathConstraint<'tcx>> {
102 self.path_constraints.get(&def_id).cloned()
103 }
104
105 fn get_all_path_constraints(&self) -> PathConstraintMap<'tcx> {
106 self.path_constraints.clone()
107 }
108}
109
110impl<'tcx, T> RangeAnalyzer<'tcx, T>
111where
112 T: IntervalArithmetic + ConstConvert + Debug,
113{
114 pub fn new(tcx: TyCtxt<'tcx>, debug: bool) -> Self {
115 let mut ssa_id = None;
116 let mut essa_id = None;
117
118 if let Some(ssa_def_id) = tcx.hir_crate_items(()).free_items().find(|id| {
119 let hir_id = id.hir_id();
120 if let Some(ident_name) = tcx.hir_opt_name(hir_id) {
121 ident_name.to_string() == "SSAstmt"
122 } else {
123 false
124 }
125 }) {
126 ssa_id = Some(ssa_def_id.owner_id.to_def_id());
127 if let Some(essa_def_id) = tcx.hir_crate_items(()).free_items().find(|id| {
128 let hir_id = id.hir_id();
129 if let Some(ident_name) = tcx.hir_opt_name(hir_id) {
130 ident_name.to_string() == "ESSAstmt"
131 } else {
132 false
133 }
134 }) {
135 essa_id = Some(essa_def_id.owner_id.to_def_id());
136 }
137 }
138 Self {
139 tcx: tcx,
140 debug,
141 ssa_def_id: ssa_id,
142 essa_def_id: essa_id,
143 final_vars: FxHashMap::default(),
144 ssa_places_mapping: FxHashMap::default(),
145 fn_constraintgraph_mapping: FxHashMap::default(),
146 callgraph: CallGraphInfo::new(),
147 body_map: FxHashMap::default(),
148 cg_map: FxHashMap::default(),
149 vars_map: FxHashMap::default(),
150 final_vars_vec: FxHashMap::default(),
151 path_constraints: FxHashMap::default(),
152 }
153 }
154
155 fn build_constraintgraph(&mut self, body_mut_ref: &'tcx Body<'tcx>, def_id: DefId) {
156 let ssa_def_id = self.ssa_def_id.expect("SSA definition ID is not set");
157 let essa_def_id = self.essa_def_id.expect("ESSA definition ID is not set");
158 let mut cg: ConstraintGraph<'tcx, T> =
159 ConstraintGraph::new(body_mut_ref, self.tcx, def_id, essa_def_id, ssa_def_id);
160 cg.build_graph(body_mut_ref);
161 cg.build_nuutila(false);
162 let dot_output = cg.to_dot();
165 let vars_map = cg.get_vars().clone();
166
167 self.cg_map.insert(def_id, Rc::new(RefCell::new(cg)));
168 let mut vec = Vec::new();
169 vec.push(RefCell::new(vars_map));
170 self.vars_map.insert(def_id, vec);
171 let function_name = self.tcx.def_path_str(def_id);
172
173 let dir_path = PathBuf::from("cg_dot");
174 fs::create_dir_all(dir_path.clone()).unwrap();
175 let safe_filename = format!("{}_cg.dot", function_name);
176 let output_path = dir_path.join(format!("{}", safe_filename));
177
178 let mut file = File::create(&output_path).expect("cannot create file");
179 file.write_all(dot_output.as_bytes())
180 .expect("Could not write to file");
181
182 println!("Successfully generated graph.dot");
183 }
185
186 fn only_caller_range_analysis(&mut self) {
187 let ssa_def_id = self.ssa_def_id.expect("SSA definition ID is not set");
188 let essa_def_id = self.essa_def_id.expect("ESSA definition ID is not set");
189 rap_debug!("PHASE 1: Building all ConstraintGraphs and the CallGraph...");
193 for local_def_id in self.tcx.iter_local_def_id() {
194 if matches!(self.tcx.def_kind(local_def_id), DefKind::Fn) {
195 let def_id = local_def_id.to_def_id();
196
197 if self.tcx.is_mir_available(def_id) {
198 let mut body = self.tcx.optimized_mir(def_id).clone();
199 let body_mut_ref = unsafe { &mut *(&mut body as *mut Body<'tcx>) };
200 let mut passrunner = PassRunner::new(self.tcx);
202 passrunner.run_pass(body_mut_ref, ssa_def_id, essa_def_id);
203 self.body_map.insert(def_id, body);
204 rap_debug!("{:#?}", body_mut_ref.local_decls);
206 if self.debug {
207 print_diff(self.tcx, body_mut_ref, def_id.into());
208 print_mir_graph(self.tcx, body_mut_ref, def_id.into());
209 }
210
211 self.ssa_places_mapping
212 .insert(def_id, passrunner.places_map.clone());
213 self.build_constraintgraph(body_mut_ref, def_id);
216 let mut call_graph_visitor =
218 CallGraphVisitor::new(self.tcx, def_id, body_mut_ref, &mut self.callgraph);
219 call_graph_visitor.visit();
220 }
221 }
222 }
223 rap_debug!("PHASE 1 Complete. CallGraph built.");
224 rap_debug!("PHASE 2: Finding and analyzing call chain start functions...");
230
231 let mut call_chain_starts: Vec<DefId> = Vec::new();
232
233 let callers_by_callee_id = self.callgraph.get_callers_map();
234
235 for (&node_id_usize, node) in &self.callgraph.functions {
236 let def_id = node.get_def_id();
237
238 if !callers_by_callee_id.contains_key(&node_id_usize)
239 && self.cg_map.contains_key(&def_id)
240 {
241 call_chain_starts.push(def_id);
242 }
243 }
244
245 call_chain_starts.sort_by_key(|d| self.tcx.def_path_str(*d));
246
247 rap_debug!(
248 "Found call chain starts ({} functions): {:?}",
249 call_chain_starts.len(),
250 call_chain_starts
251 .iter()
252 .map(|d| self.tcx.def_path_str(*d))
253 .collect::<Vec<_>>()
254 );
255
256 for def_id in call_chain_starts {
257 rap_debug!(
258 "Analyzing function (call chain start): {}",
259 self.tcx.def_path_str(def_id)
260 );
261 if let Some(cg_cell) = self.cg_map.get(&def_id) {
262 let mut cg = cg_cell.borrow_mut();
263 cg.find_intervals(&self.cg_map, &mut self.vars_map);
264 } else {
265 rap_debug!(
266 "Warning: No ConstraintGraph found for DefId {:?} during analysis of call chain starts.",
267 def_id
268 );
269 }
270 }
271
272 let analysis_order = self.callgraph.get_reverse_post_order();
273 for def_id in analysis_order {
274 if let Some(cg_cell) = self.cg_map.get(&def_id) {
275 let mut cg = cg_cell.borrow_mut();
276 let (final_vars_for_fn, _) = cg.build_final_vars(&self.ssa_places_mapping[&def_id]);
277 let mut ranges_for_fn = HashMap::new();
278 for (&place, varnode) in final_vars_for_fn {
279 ranges_for_fn.insert(place, varnode.get_range().clone());
280 }
281 let Some(varnodes_vec) = self.vars_map.get_mut(&def_id) else {
282 rap_debug!(
283 "Warning: No VarNodes found for DefId {:?} during analysis of call chain starts.",
284 def_id
285 );
286 continue;
287 };
288 for varnodes in varnodes_vec.iter_mut() {
289 let ranges_for_fn_recursive = ConstraintGraph::filter_final_vars(
290 &varnodes.borrow(),
291 &self.ssa_places_mapping[&def_id],
292 );
293 self.final_vars_vec
294 .entry(def_id)
295 .or_default()
296 .push(ranges_for_fn_recursive);
297 }
298
299 self.final_vars.insert(def_id, ranges_for_fn);
300 }
301 }
302
303 rap_debug!("PHASE 2 Complete. Interval analysis finished for call chain start functions.");
304 }
305 pub fn start_path_constraints_analysis_for_defid(
306 &mut self,
307 def_id: DefId,
308 ) -> Option<PathConstraint<'tcx>> {
309 if self.tcx.is_mir_available(def_id) {
310 let mut body = self.tcx.optimized_mir(def_id).clone();
311 let body_mut_ref = unsafe { &mut *(&mut body as *mut Body<'tcx>) };
312
313 let mut cg: ConstraintGraph<'tcx, T> =
314 ConstraintGraph::new_without_ssa(body_mut_ref, self.tcx, def_id);
315 let mut graph = MopGraph::new(self.tcx, def_id);
316 graph.find_scc();
317 let paths: Vec<Vec<usize>> = graph.get_all_branch_sub_blocks_paths();
318 let result = cg.start_analyze_path_constraints(body_mut_ref, &paths);
319 rap_debug!(
320 "Paths for function {}: {:?}",
321 self.tcx.def_path_str(def_id),
322 paths
323 );
324 let switchbbs = cg.switchbbs.clone();
325 rap_debug!(
326 "Switch basicblocks for function {}: {:?}",
327 self.tcx.def_path_str(def_id),
328 switchbbs
329 );
330 rap_debug!(
331 "Path Constraints Analysis Result for function {}: {:?}",
332 self.tcx.def_path_str(def_id),
333 result
334 );
335 self.path_constraints.insert(def_id, result.clone());
336 Some(result)
337 } else {
338 None
339 }
340 }
341 pub fn start_path_constraints_analysis(&mut self) {
342 for local_def_id in self.tcx.iter_local_def_id() {
343 if matches!(self.tcx.def_kind(local_def_id), DefKind::Fn) {
344 let def_id = local_def_id.to_def_id();
345
346 if self.tcx.is_mir_available(def_id) {
347 let mut body = self.tcx.optimized_mir(def_id).clone();
348 let body_mut_ref = unsafe { &mut *(&mut body as *mut Body<'tcx>) };
349
350 let mut cg: ConstraintGraph<'tcx, T> =
351 ConstraintGraph::new_without_ssa(body_mut_ref, self.tcx, def_id);
352 let mut graph = MopGraph::new(self.tcx, def_id);
353 graph.find_scc();
354 let paths: Vec<Vec<usize>> = graph.get_all_branch_sub_blocks_paths();
358 let result = cg.start_analyze_path_constraints(body_mut_ref, &paths);
359 rap_debug!(
360 "Paths for function {}: {:?}",
361 self.tcx.def_path_str(def_id),
362 paths
363 );
364 let switchbbs = cg.switchbbs.clone();
365 rap_debug!(
366 "Switch basicblocks for function {}: {:?}",
367 self.tcx.def_path_str(def_id),
368 switchbbs
369 );
370 rap_debug!(
371 "Path Constraints Analysis Result for function {}: {:?}",
372 self.tcx.def_path_str(def_id),
373 result
374 );
375 self.path_constraints.insert(def_id, result);
376 }
377 }
378 }
379 }
380}