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