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;
9use crate::limit::MAX_VALUES_PER_PATH;
10
11#[derive(Clone, Debug)]
24pub struct PtsGraph {
25 points_to: Vec<FxHashSet<AbstractLoc>>,
26 value_flow: Vec<FxHashSet<usize>>,
27 slots: Vec<Slot>,
28 slot_index: FxHashMap<Slot, usize>,
29 may_drop: Vec<bool>,
30 need_drop: Vec<bool>,
31 slot_kind: Vec<ValueKind>,
33
34 alias_parent: Vec<usize>,
38
39 may_hold: FxHashSet<(usize, usize)>,
42}
43
44impl PtsGraph {
45 pub fn new() -> Self {
46 PtsGraph {
47 points_to: Vec::new(),
48 value_flow: Vec::new(),
49 slots: Vec::new(),
50 slot_index: FxHashMap::default(),
51 may_drop: Vec::new(),
52 need_drop: Vec::new(),
53 slot_kind: Vec::new(),
54 alias_parent: Vec::new(),
55 may_hold: FxHashSet::default(),
56 }
57 }
58
59 pub fn slot_count(&self) -> usize {
60 self.slots.len()
61 }
62
63 pub fn get_slot(&self, idx: usize) -> Option<&Slot> {
64 self.slots.get(idx)
65 }
66
67 pub fn get_slot_idx(&self, slot: &Slot) -> Option<usize> {
68 self.slot_index.get(slot).copied()
69 }
70
71 pub fn may_drop(&self, idx: usize) -> bool {
72 self.may_drop.get(idx).copied().unwrap_or(false)
73 }
74
75 pub fn need_drop(&self, idx: usize) -> bool {
76 self.need_drop.get(idx).copied().unwrap_or(false)
77 }
78
79 pub fn ensure_slot(&mut self, slot: Slot, may_drop: bool, need_drop: bool) -> usize {
82 if let Some(&idx) = self.slot_index.get(&slot) {
83 return idx;
84 }
85 if self.slots.len() >= MAX_VALUES_PER_PATH {
86 return 0;
87 }
88 let idx = self.slots.len();
89 self.slots.push(slot.clone());
90 self.slot_index.insert(slot, idx);
91 self.points_to.push(FxHashSet::default());
92 self.value_flow.push(FxHashSet::default());
93 self.may_drop.push(may_drop);
94 self.need_drop.push(need_drop);
95 self.slot_kind.push(ValueKind::Adt);
96 self.alias_parent.push(idx); idx
98 }
99
100 pub fn set_slot_kind(&mut self, idx: usize, kind: ValueKind) {
101 if idx < self.slot_kind.len() {
102 self.slot_kind[idx] = kind;
103 }
104 }
105
106 pub fn slot_kind(&self, idx: usize) -> ValueKind {
107 self.slot_kind.get(idx).copied().unwrap_or(ValueKind::Adt)
108 }
109
110 pub fn slot_is_ptr(&self, idx: usize) -> bool {
111 matches!(self.slot_kind(idx), ValueKind::RawPtr | ValueKind::Ref)
112 }
113
114 pub fn slot_is_ref_count(&self, idx: usize) -> bool {
115 matches!(self.slot_kind(idx), ValueKind::SpecialPtr)
116 }
117
118 pub fn direct_pointees(&self, idx: usize) -> impl Iterator<Item = &AbstractLoc> {
122 self.points_to[idx].iter()
123 }
124
125 pub fn assign_pointee(&mut self, dest_idx: usize, target: AbstractLoc) {
128 self.points_to[dest_idx].clear();
129 self.points_to[dest_idx].insert(target);
130 }
131
132 pub fn assign_value(&mut self, dest_idx: usize, src_idx: usize) {
138 self.value_flow[dest_idx].clear();
139 self.value_flow[dest_idx].insert(src_idx);
140
141 self.alias_move_to_partition(dest_idx, src_idx);
143
144 if dest_idx < self.slots.len() && src_idx < self.slots.len() {
146 let dest_slot = self.slots[dest_idx].clone();
147 let src_slot = self.slots[src_idx].clone();
148
149 let dest_prefix = &dest_slot.fields;
153 let mut field_pairs: Vec<(usize, usize)> = Vec::new();
154 for (cand, cand_s) in self.slots.iter().enumerate() {
155 if cand_s.local != dest_slot.local {
156 continue;
157 }
158 if cand_s.fields.len() <= dest_prefix.len() {
159 continue;
160 }
161 if cand_s.fields[..dest_prefix.len()] != *dest_prefix {
162 continue;
163 }
164 let suffix = &cand_s.fields[dest_prefix.len()..];
166 let mut src_sub_slot = Slot::new(src_slot.local);
167 src_sub_slot.fields = src_slot.fields.clone();
168 src_sub_slot.fields.extend_from_slice(suffix);
169 if let Some(&src_sub_idx) = self.slot_index.get(&src_sub_slot) {
170 field_pairs.push((cand, src_sub_idx));
171 }
172 }
173 for (dest_cand, src_field_idx) in field_pairs {
174 self.value_flow[dest_cand].clear();
175 self.value_flow[dest_cand].insert(src_field_idx);
176 self.alias_move_to_partition(dest_cand, src_field_idx);
177 }
178 }
179 }
180
181 pub fn merge_equivalence(&mut self, a_idx: usize, b_idx: usize) {
187 if a_idx == b_idx {
188 return;
189 }
190 let a_pts: Vec<_> = self.points_to[a_idx].iter().cloned().collect();
192 for loc in a_pts {
193 self.points_to[b_idx].insert(loc);
194 }
195 let b_pts: Vec<_> = self.points_to[b_idx].iter().cloned().collect();
196 for loc in b_pts {
197 self.points_to[a_idx].insert(loc);
198 }
199
200 self.alias_union(a_idx, b_idx);
202
203 self.propagate_to_father(a_idx, b_idx);
206 }
207
208 fn propagate_to_father(&mut self, a_idx: usize, b_idx: usize) {
209 let fa = self.father_of(a_idx);
210 let fb = self.father_of(b_idx);
211 let ra = fa.unwrap_or(a_idx);
212 let rb = fb.unwrap_or(b_idx);
213 if self.alias_find(ra) != self.alias_find(rb) {
214 self.alias_union(ra, rb);
215 }
216 }
217
218 fn father_of(&self, idx: usize) -> Option<usize> {
219 let slot = &self.slots[idx];
220 if slot.fields.is_empty() {
221 return None;
222 }
223 let father_slot = Slot {
224 local: slot.local,
225 fields: slot.fields[..slot.fields.len() - 1].to_vec(),
226 };
227 self.slot_index.get(&father_slot).copied()
228 }
229
230 pub fn hold(&mut self, holder: usize, held: usize) {
233 if holder == held {
234 return;
235 }
236 let held_pts: Vec<_> = self.points_to[held].iter().cloned().collect();
237 self.points_to[holder].extend(held_pts);
238 self.may_hold.insert((holder, self.alias_find(held)));
239 let father_holder = self.father_of(holder).unwrap_or(holder);
240 let father_held = self.alias_find(self.father_of(held).unwrap_or(held));
241 self.may_hold.insert((father_holder, father_held));
242 }
243
244 pub fn conservative_call_merge(&mut self, arg_slots: &[usize]) {
247 let mut pointer_args: Vec<usize> = Vec::new();
248 for &idx in arg_slots {
249 if !self.points_to[idx].is_empty() {
250 pointer_args.push(idx);
251 } else if self.may_drop(idx) {
252 pointer_args.push(idx);
253 }
254 }
255 for i in 0..pointer_args.len() {
256 for j in (i + 1)..pointer_args.len() {
257 self.merge_equivalence(pointer_args[i], pointer_args[j]);
258 }
259 }
260 }
261
262 pub fn pts(&self, start_idx: usize) -> FxHashSet<AbstractLoc> {
267 let mut result = FxHashSet::default();
268 let mut visited = FxHashSet::default();
269 let mut queue = VecDeque::new();
270 queue.push_back(Start::Pointee(start_idx));
271 visited.insert(Visit::Pointee(start_idx));
272
273 while let Some(current) = queue.pop_front() {
274 match current {
275 Start::Pointee(idx) => {
276 for loc in &self.points_to[idx] {
277 if !matches!(loc, AbstractLoc::Null) {
278 result.insert(loc.clone());
279 }
280 }
281 for &src in &self.value_flow[idx] {
282 if visited.insert(Visit::Pointee(src)) {
283 queue.push_back(Start::Pointee(src));
284 }
285 }
286 }
287 }
288 }
289 result
290 }
291
292 pub fn may_alias(&self, a_idx: usize, b_idx: usize) -> bool {
297 if self.alias_find(a_idx) == self.alias_find(b_idx) {
299 return true;
300 }
301 if !self.may_hold.is_empty() {
302 let held_a = self.held_partitions(a_idx);
303 if self
304 .held_partitions(b_idx)
305 .iter()
306 .any(|root| held_a.contains(root))
307 {
308 return true;
309 }
310 }
311 let pta = self.pts(a_idx);
313 if pta.is_empty() {
314 return false;
315 }
316 let ptb = self.pts(b_idx);
317 pta.intersection(&ptb).next().is_some()
318 }
319
320 pub fn apply_callee_summary(
325 &mut self,
326 callee_pairs: &crate::analysis::alias::FnAliasPairs,
327 callee_arg_slots: &[usize],
328 ) {
329 let ret_sources: FxHashSet<usize> = callee_pairs
330 .aliases()
331 .iter()
332 .filter(|alias| alias.left_local() == 0)
333 .map(|alias| alias.right_local())
334 .collect();
335 for alias in callee_pairs.aliases() {
336 let left_idx = alias.left_local();
337 let right_idx = alias.right_local();
338
339 if left_idx >= callee_arg_slots.len() || right_idx >= callee_arg_slots.len() {
340 continue;
341 }
342
343 let mut lv = callee_arg_slots[left_idx];
344 let mut rv = callee_arg_slots[right_idx];
345
346 for &field_idx in alias.lhs_fields() {
347 let field_slot = self.slots[lv].project(field_idx);
348 if let Some(idx) = self.slot_index.get(&field_slot) {
349 lv = *idx;
350 } else {
351 let idx = self.ensure_slot(field_slot, self.may_drop[lv], self.need_drop[lv]);
352 lv = idx;
353 }
354 }
355 for &field_idx in alias.rhs_fields() {
356 let field_slot = self.slots[rv].project(field_idx);
357 if let Some(idx) = self.slot_index.get(&field_slot) {
358 rv = *idx;
359 } else {
360 let idx = self.ensure_slot(field_slot, self.may_drop[rv], self.need_drop[rv]);
361 rv = idx;
362 }
363 }
364
365 if !self.may_drop(lv) || !self.may_drop(rv) {
366 continue;
367 }
368 if left_idx == 0 && ret_sources.len() > 1 {
369 self.hold(lv, rv);
370 } else {
371 self.merge_equivalence(lv, rv);
372 }
373 }
374 }
375
376 pub fn fn_alias_pairs(&self, arg_count: usize) -> crate::analysis::alias::FnAliasPairs {
382 let mut pairs = crate::analysis::alias::FnAliasPairs::new(arg_count);
383
384 let local_ids: Vec<usize> = (0..=arg_count).collect();
385
386 let mut local_to_base_slot: FxHashMap<usize, usize> = FxHashMap::default();
388 for (slot_idx, s) in self.slots.iter().enumerate() {
389 if s.fields.is_empty() && s.local <= arg_count {
390 local_to_base_slot.entry(s.local).or_insert(slot_idx);
391 }
392 }
393
394 for i in 0..local_ids.len() {
396 for j in (i + 1)..local_ids.len() {
397 let li = local_ids[i];
398 let lj = local_ids[j];
399 let Some(&slot_i) = local_to_base_slot.get(&li) else {
400 continue;
401 };
402 let Some(&slot_j) = local_to_base_slot.get(&lj) else {
403 continue;
404 };
405 if self.may_alias(slot_i, slot_j) {
406 let mut pair = crate::analysis::alias::AliasPair::new(li, lj);
407 pair.lhs_fields = vec![];
408 pair.rhs_fields = vec![];
409 pairs.add_alias(pair);
410 }
411 }
412 }
413
414 let field_slots: Vec<(usize, Vec<usize>)> = self
416 .slots
417 .iter()
418 .enumerate()
419 .filter_map(|(idx, slot)| {
420 if !slot.fields.is_empty() && slot.local <= arg_count {
421 Some((idx, slot.fields.clone()))
422 } else {
423 None
424 }
425 })
426 .collect();
427
428 for (idx_a, fields_a) in &field_slots {
429 let slot_a = &self.slots[*idx_a];
430 for (idx_b, fields_b) in &field_slots {
432 if idx_a == idx_b {
433 continue;
434 }
435 let slot_b = &self.slots[*idx_b];
436 if slot_a.local == slot_b.local {
437 continue;
438 }
439 if self.may_alias(*idx_a, *idx_b) {
440 let mut pair =
441 crate::analysis::alias::AliasPair::new(slot_a.local, slot_b.local);
442 pair.lhs_fields = fields_a.clone();
443 pair.rhs_fields = fields_b.clone();
444 pairs.add_alias(pair);
445 }
446 }
447 for &base_local in &local_ids {
449 if slot_a.local == base_local {
450 continue;
451 }
452 let Some(&base_slot_idx) = local_to_base_slot.get(&base_local) else {
453 continue;
454 };
455 if self.may_alias(*idx_a, base_slot_idx) {
456 let mut pair = crate::analysis::alias::AliasPair::new(slot_a.local, base_local);
457 pair.lhs_fields = fields_a.clone();
458 pair.rhs_fields = vec![];
459 pairs.add_alias(pair);
460 }
461 }
462 }
463
464 pairs.compress_fields();
467
468 pairs.sort_alias_index();
469 pairs
470 }
471
472 fn alias_find(&self, idx: usize) -> usize {
476 if idx >= self.alias_parent.len() {
477 return idx;
478 }
479 let mut cur = idx;
480 while self.alias_parent[cur] != cur {
481 cur = self.alias_parent[cur];
482 }
483 cur
484 }
485
486 fn alias_union(&mut self, a: usize, b: usize) {
488 let ra = self.alias_find(a);
489 let rb = self.alias_find(b);
490 if ra != rb {
491 self.alias_parent[ra] = rb;
492 }
493 }
494
495 fn held_partitions(&self, idx: usize) -> FxHashSet<usize> {
497 let mut roots = FxHashSet::default();
498 roots.insert(self.alias_find(idx));
499 let mut changed = true;
500 while changed {
501 changed = false;
502 for &(holder, held) in &self.may_hold {
503 if roots.contains(&self.alias_find(holder)) && roots.insert(self.alias_find(held)) {
504 changed = true;
505 }
506 }
507 }
508 roots
509 }
510
511 fn alias_move_to_partition(&mut self, slot_idx: usize, target_idx: usize) {
515 if slot_idx >= self.alias_parent.len() {
516 return;
517 }
518 let target_root = self.alias_find(target_idx);
520 self.alias_parent[slot_idx] = target_root;
521 self.may_hold.retain(|&(holder, _)| holder != slot_idx);
522 }
523
524 pub fn reset_partition(&mut self, slot_idx: usize) {
529 if slot_idx >= self.alias_parent.len() {
530 return;
531 }
532 let root = self.alias_find(slot_idx);
533 for i in 0..self.alias_parent.len() {
534 if self.alias_find(i) == root {
535 self.alias_parent[i] = i;
536 }
537 }
538 self.may_hold.retain(|&(holder, _)| holder != slot_idx);
539 }
540
541 pub fn insert_place_edge(&mut self, pointer: &PlaceKey, source: &PlaceKey) {
546 let ptr_slot = Self::place_key_to_slot(pointer);
547 let src_slot = Self::place_key_to_slot(source);
548 let ptr_idx = self.ensure_slot(ptr_slot, false, false);
549 self.ensure_slot(src_slot.clone(), false, false);
550 self.assign_pointee(ptr_idx, AbstractLoc::Slot(src_slot));
551 }
552
553 pub fn get_place_source(&self, place: &PlaceKey) -> Option<PlaceKey> {
556 let mut slot = Self::place_key_to_slot(place);
557 loop {
558 if let Some(idx) = self.slot_index.get(&slot) {
559 if let Some(first_loc) = self.points_to.get(*idx).and_then(|set| set.iter().next())
560 {
561 if let AbstractLoc::Slot(target) = first_loc {
562 return Some(Self::slot_to_place_key(target));
563 }
564 }
565 }
566 if slot.fields.is_empty() {
567 return None;
568 }
569 slot.fields.pop();
570 }
571 }
572
573 pub fn resolve_place(&self, place: &PlaceKey) -> PlaceKey {
576 let mut cur = place.clone();
577 let mut seen: Vec<PlaceKey> = vec![cur.clone()];
578 loop {
579 let Some(next) = self.get_place_source(&cur) else {
580 break;
581 };
582 if seen.iter().any(|p| p == &next) {
583 break;
584 }
585 seen.push(next.clone());
586 cur = next.clone();
587 }
588 cur
589 }
590
591 pub fn place_edges(&self) -> Vec<(PlaceKey, PlaceKey)> {
593 let mut edges = Vec::new();
594 for (idx, targets) in self.points_to.iter().enumerate() {
595 let Some(slot) = self.slots.get(idx) else {
596 continue;
597 };
598 let pointer = Self::slot_to_place_key(slot);
599 for target in targets {
600 if let AbstractLoc::Slot(target_slot) = target {
601 let source = Self::slot_to_place_key(target_slot);
602 edges.push((pointer.clone(), source));
603 }
604 }
605 }
606 edges
607 }
608
609 fn place_key_to_slot(pk: &PlaceKey) -> Slot {
610 let local = pk.local().map(|l| l.as_usize()).unwrap_or(0);
611 Slot {
612 local,
613 fields: pk.fields.clone(),
614 }
615 }
616
617 fn slot_to_place_key(slot: &Slot) -> PlaceKey {
618 PlaceKey {
619 base: PlaceBaseKey::Local(slot.local),
620 fields: slot.fields.clone(),
621 }
622 }
623}
624
625impl Default for PtsGraph {
626 fn default() -> Self {
627 Self::new()
628 }
629}
630
631#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
634enum Start {
635 Pointee(usize),
636}
637
638#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
639enum Visit {
640 Pointee(usize),
641}