1use std::collections::VecDeque;
2
3use crate::compat::{FxHashMap, FxHashSet};
4use crate::helpers::def_use::{PlaceBaseKey, PlaceKey};
5
6use super::slot::{AbstractLoc, Slot};
7
8use crate::analysis::alias::default::types::ValueKind;
9
10pub const MAX_VALUES_PER_PATH: usize = 1000;
11
12#[derive(Clone, Debug)]
25pub struct PtsGraph {
26 points_to: Vec<FxHashSet<AbstractLoc>>,
27 value_flow: Vec<FxHashSet<usize>>,
28 slots: Vec<Slot>,
29 slot_index: FxHashMap<Slot, usize>,
30 may_drop: Vec<bool>,
31 need_drop: Vec<bool>,
32 slot_kind: Vec<ValueKind>,
34
35 alias_parent: Vec<usize>,
39}
40
41impl PtsGraph {
42 pub fn new() -> Self {
43 PtsGraph {
44 points_to: Vec::new(),
45 value_flow: Vec::new(),
46 slots: Vec::new(),
47 slot_index: FxHashMap::default(),
48 may_drop: Vec::new(),
49 need_drop: Vec::new(),
50 slot_kind: Vec::new(),
51 alias_parent: Vec::new(),
52 }
53 }
54
55 pub fn slot_count(&self) -> usize {
56 self.slots.len()
57 }
58
59 pub fn get_slot(&self, idx: usize) -> Option<&Slot> {
60 self.slots.get(idx)
61 }
62
63 pub fn get_slot_idx(&self, slot: &Slot) -> Option<usize> {
64 self.slot_index.get(slot).copied()
65 }
66
67 pub fn may_drop(&self, idx: usize) -> bool {
68 self.may_drop.get(idx).copied().unwrap_or(false)
69 }
70
71 pub fn need_drop(&self, idx: usize) -> bool {
72 self.need_drop.get(idx).copied().unwrap_or(false)
73 }
74
75 pub fn ensure_slot(
78 &mut self,
79 slot: Slot,
80 may_drop: bool,
81 need_drop: bool,
82 ) -> usize {
83 if let Some(&idx) = self.slot_index.get(&slot) {
84 return idx;
85 }
86 if self.slots.len() >= MAX_VALUES_PER_PATH {
87 return 0;
88 }
89 let idx = self.slots.len();
90 self.slots.push(slot.clone());
91 self.slot_index.insert(slot, idx);
92 self.points_to.push(FxHashSet::default());
93 self.value_flow.push(FxHashSet::default());
94 self.may_drop.push(may_drop);
95 self.need_drop.push(need_drop);
96 self.slot_kind.push(ValueKind::Adt);
97 self.alias_parent.push(idx); idx
99 }
100
101 pub fn set_slot_kind(&mut self, idx: usize, kind: ValueKind) {
102 if idx < self.slot_kind.len() {
103 self.slot_kind[idx] = kind;
104 }
105 }
106
107 pub fn slot_kind(&self, idx: usize) -> ValueKind {
108 self.slot_kind.get(idx).copied().unwrap_or(ValueKind::Adt)
109 }
110
111 pub fn slot_is_ptr(&self, idx: usize) -> bool {
112 matches!(self.slot_kind(idx), ValueKind::RawPtr | ValueKind::Ref)
113 }
114
115 pub fn slot_is_ref_count(&self, idx: usize) -> bool {
116 matches!(self.slot_kind(idx), ValueKind::SpecialPtr)
117 }
118
119 pub fn direct_pointees(&self, idx: usize) -> impl Iterator<Item = &AbstractLoc> {
123 self.points_to[idx].iter()
124 }
125
126 pub fn assign_pointee(&mut self, dest_idx: usize, target: AbstractLoc) {
129 self.points_to[dest_idx].clear();
130 self.points_to[dest_idx].insert(target);
131 }
132
133 pub fn assign_value(&mut self, dest_idx: usize, src_idx: usize) {
139 self.value_flow[dest_idx].clear();
140 self.value_flow[dest_idx].insert(src_idx);
141
142 self.alias_move_to_partition(dest_idx, src_idx);
144
145 if dest_idx < self.slots.len() && src_idx < self.slots.len() {
147 let dest_slot = self.slots[dest_idx].clone();
148 let src_slot = self.slots[src_idx].clone();
149
150 let dest_prefix = &dest_slot.fields;
154 let mut field_pairs: Vec<(usize, usize)> = Vec::new();
155 for (cand, cand_s) in self.slots.iter().enumerate() {
156 if cand_s.local != dest_slot.local {
157 continue;
158 }
159 if cand_s.fields.len() <= dest_prefix.len() {
160 continue;
161 }
162 if cand_s.fields[..dest_prefix.len()] != *dest_prefix {
163 continue;
164 }
165 let suffix = &cand_s.fields[dest_prefix.len()..];
167 let mut src_sub_slot = Slot::new(src_slot.local);
168 src_sub_slot.fields = src_slot.fields.clone();
169 src_sub_slot.fields.extend_from_slice(suffix);
170 if let Some(&src_sub_idx) = self.slot_index.get(&src_sub_slot) {
171 field_pairs.push((cand, src_sub_idx));
172 }
173 }
174 for (dest_cand, src_field_idx) in field_pairs {
175 self.value_flow[dest_cand].clear();
176 self.value_flow[dest_cand].insert(src_field_idx);
177 self.alias_move_to_partition(dest_cand, src_field_idx);
178 }
179 }
180 }
181
182 pub fn merge_equivalence(&mut self, a_idx: usize, b_idx: usize) {
188 if a_idx == b_idx {
189 return;
190 }
191 let a_pts: Vec<_> = self.points_to[a_idx].iter().cloned().collect();
193 for loc in a_pts {
194 self.points_to[b_idx].insert(loc);
195 }
196 let b_pts: Vec<_> = self.points_to[b_idx].iter().cloned().collect();
197 for loc in b_pts {
198 self.points_to[a_idx].insert(loc);
199 }
200
201 self.alias_union(a_idx, b_idx);
203
204 self.propagate_to_father(a_idx, b_idx);
207 }
208
209 fn propagate_to_father(&mut self, a_idx: usize, b_idx: usize) {
210 let fa = self.father_of(a_idx);
211 let fb = self.father_of(b_idx);
212 let ra = fa.unwrap_or(a_idx);
213 let rb = fb.unwrap_or(b_idx);
214 if self.alias_find(ra) != self.alias_find(rb) {
215 self.alias_union(ra, rb);
216 }
217 }
218
219 fn father_of(&self, idx: usize) -> Option<usize> {
220 let slot = &self.slots[idx];
221 if slot.fields.is_empty() {
222 return None;
223 }
224 let father_slot = Slot {
225 local: slot.local,
226 fields: slot.fields[..slot.fields.len() - 1].to_vec(),
227 };
228 self.slot_index.get(&father_slot).copied()
229 }
230
231 pub fn conservative_call_merge(&mut self, arg_slots: &[usize]) {
234 let mut pointer_args: Vec<usize> = Vec::new();
235 for &idx in arg_slots {
236 if !self.points_to[idx].is_empty() {
237 pointer_args.push(idx);
238 } else if self.may_drop(idx) {
239 pointer_args.push(idx);
240 }
241 }
242 for i in 0..pointer_args.len() {
243 for j in (i + 1)..pointer_args.len() {
244 self.merge_equivalence(pointer_args[i], pointer_args[j]);
245 }
246 }
247 }
248
249 pub fn pts(&self, start_idx: usize) -> FxHashSet<AbstractLoc> {
254 let mut result = FxHashSet::default();
255 let mut visited = FxHashSet::default();
256 let mut queue = VecDeque::new();
257 queue.push_back(Start::Pointee(start_idx));
258 visited.insert(Visit::Pointee(start_idx));
259
260 while let Some(current) = queue.pop_front() {
261 match current {
262 Start::Pointee(idx) => {
263 for loc in &self.points_to[idx] {
264 if !matches!(loc, AbstractLoc::Null) {
265 result.insert(loc.clone());
266 }
267 }
268 for &src in &self.value_flow[idx] {
269 if visited.insert(Visit::Pointee(src)) {
270 queue.push_back(Start::Pointee(src));
271 }
272 }
273 }
274 }
275 }
276 result
277 }
278
279 pub fn may_alias(&self, a_idx: usize, b_idx: usize) -> bool {
284 if self.alias_find(a_idx) == self.alias_find(b_idx) {
286 return true;
287 }
288 let pta = self.pts(a_idx);
290 if pta.is_empty() {
291 return false;
292 }
293 let ptb = self.pts(b_idx);
294 pta.intersection(&ptb).next().is_some()
295 }
296
297 pub fn apply_callee_summary(
302 &mut self,
303 callee_pairs: &crate::analysis::alias::FnAliasPairs,
304 callee_arg_slots: &[usize],
305 ) {
306 for alias in callee_pairs.aliases() {
307 let left_idx = alias.left_local();
308 let right_idx = alias.right_local();
309
310 if left_idx >= callee_arg_slots.len() || right_idx >= callee_arg_slots.len() {
311 continue;
312 }
313
314 let mut lv = callee_arg_slots[left_idx];
315 let mut rv = callee_arg_slots[right_idx];
316
317 for &field_idx in alias.lhs_fields() {
318 let field_slot = self.slots[lv].project(field_idx);
319 if let Some(idx) = self.slot_index.get(&field_slot) {
320 lv = *idx;
321 } else {
322 let idx = self.ensure_slot(
323 field_slot,
324 self.may_drop[lv],
325 self.need_drop[lv],
326 );
327 lv = idx;
328 }
329 }
330 for &field_idx in alias.rhs_fields() {
331 let field_slot = self.slots[rv].project(field_idx);
332 if let Some(idx) = self.slot_index.get(&field_slot) {
333 rv = *idx;
334 } else {
335 let idx = self.ensure_slot(
336 field_slot,
337 self.may_drop[rv],
338 self.need_drop[rv],
339 );
340 rv = idx;
341 }
342 }
343
344 if self.may_drop(lv) && self.may_drop(rv) {
345 self.merge_equivalence(lv, rv);
346 }
347 }
348 }
349
350 pub fn fn_alias_pairs(
356 &self,
357 arg_count: usize,
358 ) -> crate::analysis::alias::FnAliasPairs {
359 let mut pairs = crate::analysis::alias::FnAliasPairs::new(arg_count);
360
361 let local_ids: Vec<usize> = (0..=arg_count).collect();
362
363 let mut local_to_base_slot: FxHashMap<usize, usize> = FxHashMap::default();
365 for (slot_idx, s) in self.slots.iter().enumerate() {
366 if s.fields.is_empty() && s.local <= arg_count {
367 local_to_base_slot.entry(s.local).or_insert(slot_idx);
368 }
369 }
370
371 for i in 0..local_ids.len() {
373 for j in (i + 1)..local_ids.len() {
374 let li = local_ids[i];
375 let lj = local_ids[j];
376 let Some(&slot_i) = local_to_base_slot.get(&li) else { continue; };
377 let Some(&slot_j) = local_to_base_slot.get(&lj) else { continue; };
378 if self.may_alias(slot_i, slot_j) {
379 let mut pair =
380 crate::analysis::alias::AliasPair::new(li, lj);
381 pair.lhs_fields = vec![];
382 pair.rhs_fields = vec![];
383 pairs.add_alias(pair);
384 }
385 }
386 }
387
388 let field_slots: Vec<(usize, Vec<usize>)> = self
390 .slots
391 .iter()
392 .enumerate()
393 .filter_map(|(idx, slot)| {
394 if !slot.fields.is_empty() && slot.local <= arg_count {
395 Some((idx, slot.fields.clone()))
396 } else {
397 None
398 }
399 })
400 .collect();
401
402 for (idx_a, fields_a) in &field_slots {
403 let slot_a = &self.slots[*idx_a];
404 for (idx_b, fields_b) in &field_slots {
406 if idx_a == idx_b { continue; }
407 let slot_b = &self.slots[*idx_b];
408 if slot_a.local == slot_b.local { continue; }
409 if self.may_alias(*idx_a, *idx_b) {
410 let mut pair = crate::analysis::alias::AliasPair::new(slot_a.local, slot_b.local);
411 pair.lhs_fields = fields_a.clone();
412 pair.rhs_fields = fields_b.clone();
413 pairs.add_alias(pair);
414 }
415 }
416 for &base_local in &local_ids {
418 if slot_a.local == base_local { continue; }
419 let Some(&base_slot_idx) = local_to_base_slot.get(&base_local) else { continue; };
420 if self.may_alias(*idx_a, base_slot_idx) {
421 let mut pair = crate::analysis::alias::AliasPair::new(slot_a.local, base_local);
422 pair.lhs_fields = fields_a.clone();
423 pair.rhs_fields = vec![];
424 pairs.add_alias(pair);
425 }
426 }
427 }
428
429 pairs.compress_fields();
432
433 pairs.sort_alias_index();
434 pairs
435 }
436
437 fn alias_find(&self, idx: usize) -> usize {
441 if idx >= self.alias_parent.len() {
442 return idx;
443 }
444 let mut cur = idx;
445 while self.alias_parent[cur] != cur {
446 cur = self.alias_parent[cur];
447 }
448 cur
449 }
450
451 fn alias_union(&mut self, a: usize, b: usize) {
453 let ra = self.alias_find(a);
454 let rb = self.alias_find(b);
455 if ra != rb {
456 self.alias_parent[ra] = rb;
457 }
458 }
459
460 fn alias_move_to_partition(&mut self, slot_idx: usize, target_idx: usize) {
464 if slot_idx >= self.alias_parent.len() {
465 return;
466 }
467 let target_root = self.alias_find(target_idx);
469 self.alias_parent[slot_idx] = target_root;
470 }
471
472 pub fn reset_partition(&mut self, slot_idx: usize) {
477 if slot_idx >= self.alias_parent.len() {
478 return;
479 }
480 let root = self.alias_find(slot_idx);
481 for i in 0..self.alias_parent.len() {
482 if self.alias_find(i) == root {
483 self.alias_parent[i] = i;
484 }
485 }
486 }
487
488 pub fn insert_place_edge(&mut self, pointer: &PlaceKey, source: &PlaceKey) {
493 let ptr_slot = Self::place_key_to_slot(pointer);
494 let src_slot = Self::place_key_to_slot(source);
495 let ptr_idx = self.ensure_slot(ptr_slot, false, false);
496 self.ensure_slot(src_slot.clone(), false, false);
497 self.assign_pointee(ptr_idx, AbstractLoc::Slot(src_slot));
498 }
499
500 pub fn get_place_source(&self, place: &PlaceKey) -> Option<PlaceKey> {
503 let mut slot = Self::place_key_to_slot(place);
504 loop {
505 if let Some(idx) = self.slot_index.get(&slot) {
506 if let Some(first_loc) =
507 self.points_to.get(*idx).and_then(|set| set.iter().next())
508 {
509 if let AbstractLoc::Slot(target) = first_loc {
510 return Some(Self::slot_to_place_key(target));
511 }
512 }
513 }
514 if slot.fields.is_empty() {
515 return None;
516 }
517 slot.fields.pop();
518 }
519 }
520
521 pub fn resolve_place(&self, place: &PlaceKey) -> PlaceKey {
524 let mut cur = place.clone();
525 let mut seen: Vec<PlaceKey> = vec![cur.clone()];
526 loop {
527 let Some(next) = self.get_place_source(&cur) else {
528 break;
529 };
530 if seen.iter().any(|p| p == &next) {
531 break;
532 }
533 seen.push(next.clone());
534 cur = next.clone();
535 }
536 cur
537 }
538
539 pub fn place_edges(&self) -> Vec<(PlaceKey, PlaceKey)> {
541 let mut edges = Vec::new();
542 for (idx, targets) in self.points_to.iter().enumerate() {
543 let Some(slot) = self.slots.get(idx) else { continue };
544 let pointer = Self::slot_to_place_key(slot);
545 for target in targets {
546 if let AbstractLoc::Slot(target_slot) = target {
547 let source = Self::slot_to_place_key(target_slot);
548 edges.push((pointer.clone(), source));
549 }
550 }
551 }
552 edges
553 }
554
555 fn place_key_to_slot(pk: &PlaceKey) -> Slot {
556 let local = pk.local().map(|l| l.as_usize()).unwrap_or(0);
557 Slot { local, fields: pk.fields.clone() }
558 }
559
560 fn slot_to_place_key(slot: &Slot) -> PlaceKey {
561 PlaceKey {
562 base: PlaceBaseKey::Local(slot.local),
563 fields: slot.fields.clone(),
564 }
565 }
566}
567
568impl Default for PtsGraph {
569 fn default() -> Self {
570 Self::new()
571 }
572}
573
574#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
577enum Start {
578 Pointee(usize),
579}
580
581#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
582enum Visit {
583 Pointee(usize),
584}