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