1use crate::compat::FxHashMap;
2use crate::compat::Spanned;
3use rustc_hir::def_id::DefId;
4use rustc_middle::{
5 mir::{
6 Body, CallReturnPlaces, Location, Operand, Place, Rvalue, Statement, StatementKind,
7 Terminator, TerminatorEdges, TerminatorKind,
8 },
9 ty::{self, Ty, TyCtxt, TypingEnv},
10};
11use rustc_mir_dataflow::{Analysis, JoinSemiLattice, fmt::DebugWithContext};
12use std::cell::RefCell;
13use std::rc::Rc;
14
15use super::super::{FnAliasMap, FnAliasPairs};
16use super::transfer;
17use crate::analysis::alias::default::types::is_not_drop;
18
19fn apply_function_summary<'tcx>(
21 state: &mut AliasDomain,
22 destination: Place<'tcx>,
23 args: &[Operand<'tcx>],
24 summary: &FnAliasPairs,
25 place_info: &PlaceInfo<'tcx>,
26) {
27 let dest_id = transfer::mir_place_to_place_id(destination);
29
30 let mut actual_places = vec![dest_id.clone()];
33 for arg in args {
34 if let Some(arg_id) = transfer::operand_to_place_id(arg) {
35 actual_places.push(arg_id);
36 } else {
37 actual_places.push(PlaceId::Local(usize::MAX));
39 }
40 }
41
42 for alias_pair in summary.aliases() {
44 let left_idx = alias_pair.left_local();
45 let right_idx = alias_pair.right_local();
46
47 if left_idx >= actual_places.len() || right_idx >= actual_places.len() {
49 continue;
50 }
51
52 if actual_places[left_idx] == PlaceId::Local(usize::MAX)
55 || actual_places[right_idx] == PlaceId::Local(usize::MAX)
56 {
57 continue;
58 }
59
60 let mut left_place = actual_places[left_idx].clone();
62 for &field_idx in alias_pair.lhs_fields() {
63 left_place = left_place.project_field(field_idx);
64 }
65
66 let mut right_place = actual_places[right_idx].clone();
67 for &field_idx in alias_pair.rhs_fields() {
68 right_place = right_place.project_field(field_idx);
69 }
70
71 if let (Some(left_place_idx), Some(right_place_idx)) = (
73 place_info.get_index(&left_place),
74 place_info.get_index(&right_place),
75 ) {
76 let left_may_drop = place_info.may_drop(left_place_idx);
77 let right_may_drop = place_info.may_drop(right_place_idx);
78 if left_may_drop && right_may_drop {
79 state.union(left_place_idx, right_place_idx);
80 }
81 }
82 }
83}
84
85fn apply_conservative_alias_for_call<'tcx>(
88 state: &mut AliasDomain,
89 destination: Place<'tcx>,
90 args: &[Spanned<rustc_middle::mir::Operand<'tcx>>],
91 place_info: &PlaceInfo<'tcx>,
92) {
93 let dest_id = transfer::mir_place_to_place_id(destination);
95 let dest_idx = match place_info.get_index(&dest_id) {
96 Some(idx) => idx,
97 None => {
98 return;
99 }
100 };
101
102 if !place_info.may_drop(dest_idx) {
104 return;
105 }
106
107 for (_i, arg) in args.iter().enumerate() {
109 if let Some(arg_id) = transfer::operand_to_place_id(&arg.node) {
110 if let Some(arg_idx) = place_info.get_index(&arg_id) {
111 if place_info.may_drop(arg_idx) {
112 state.union(dest_idx, arg_idx);
114
115 transfer::sync_fields(state, &dest_id, &arg_id, place_info);
117 }
118 }
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Hash)]
125pub enum PlaceId {
126 Local(usize),
128 Field {
130 base: Box<PlaceId>,
131 field_idx: usize,
132 },
133}
134
135impl PlaceId {
136 pub fn root_local(&self) -> usize {
138 match self {
139 PlaceId::Local(idx) => *idx,
140 PlaceId::Field { base, .. } => base.root_local(),
141 }
142 }
143
144 pub fn project_field(&self, field_idx: usize) -> PlaceId {
146 PlaceId::Field {
147 base: Box::new(self.clone()),
148 field_idx,
149 }
150 }
151
152 pub fn has_prefix(&self, prefix: &PlaceId) -> bool {
155 if self == prefix {
156 return true;
157 }
158
159 match self {
160 PlaceId::Local(_) => false,
161 PlaceId::Field { base, .. } => base.has_prefix(prefix),
162 }
163 }
164}
165
166#[derive(Clone)]
168pub struct PlaceInfo<'tcx> {
169 place_to_index: FxHashMap<PlaceId, usize>,
171 index_to_place: Vec<PlaceId>,
173 may_drop: Vec<bool>,
175 need_drop: Vec<bool>,
177 num_places: usize,
179 _phantom: std::marker::PhantomData<&'tcx ()>,
180}
181
182impl<'tcx> PlaceInfo<'tcx> {
183 pub fn new() -> Self {
185 PlaceInfo {
186 place_to_index: FxHashMap::default(),
187 index_to_place: Vec::new(),
188 may_drop: Vec::new(),
189 need_drop: Vec::new(),
190 num_places: 0,
191 _phantom: std::marker::PhantomData,
192 }
193 }
194
195 pub fn build(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) -> Self {
197 let mut info = Self::new();
198 let ty_env = TypingEnv::post_analysis(tcx, def_id);
199
200 for (local, local_decl) in body.local_decls.iter_enumerated() {
202 let ty = local_decl.ty;
203 let need_drop = ty.needs_drop(tcx, ty_env);
204 let may_drop = !is_not_drop(tcx, ty);
205
206 let place_id = PlaceId::Local(local.as_usize());
207 info.register_place(place_id.clone(), may_drop, need_drop);
208
209 info.create_fields_for_type(tcx, ty, place_id, 0, 0, ty_env);
211 }
212
213 info
214 }
215
216 fn create_fields_for_type(
218 &mut self,
219 tcx: TyCtxt<'tcx>,
220 ty: Ty<'tcx>,
221 base_place: PlaceId,
222 field_depth: usize,
223 deref_depth: usize,
224 ty_env: TypingEnv<'tcx>,
225 ) {
226 const MAX_FIELD_DEPTH: usize = 5;
228 const MAX_DEREF_DEPTH: usize = 3;
229 if field_depth >= MAX_FIELD_DEPTH || deref_depth >= MAX_DEREF_DEPTH {
230 return;
231 }
232
233 match ty.kind() {
234 ty::Ref(_, inner_ty, _) => {
237 self.create_fields_for_type(
238 tcx,
239 *inner_ty,
240 base_place,
241 field_depth,
242 deref_depth + 1,
243 ty_env,
244 );
245 }
246 ty::RawPtr(inner_ty, _) => {
248 self.create_fields_for_type(
249 tcx,
250 *inner_ty,
251 base_place,
252 field_depth,
253 deref_depth + 1,
254 ty_env,
255 );
256 }
257 ty::Adt(adt_def, substs) => {
259 for (field_idx, field) in adt_def.all_fields().enumerate() {
260 #[cfg(not(rapx_ge_99))]
261 let field_ty = field.ty(tcx, substs);
262 #[cfg(rapx_ge_99)]
263 let field_ty = field.ty(tcx, substs).skip_norm_wip();
264 let field_place = base_place.project_field(field_idx);
265
266 let need_drop = field_ty.needs_drop(tcx, ty_env);
269
270 let may_drop = if deref_depth > 0 {
276 true
277 } else {
278 !is_not_drop(tcx, field_ty)
279 };
280
281 self.register_place(field_place.clone(), may_drop, need_drop);
282
283 self.create_fields_for_type(
285 tcx,
286 field_ty,
287 field_place,
288 field_depth + 1,
289 deref_depth,
290 ty_env,
291 );
292 }
293 }
294 ty::Tuple(fields) => {
296 for (field_idx, field_ty) in fields.iter().enumerate() {
297 let field_place = base_place.project_field(field_idx);
298
299 let may_drop = if deref_depth > 0 {
306 true
307 } else {
308 !is_not_drop(tcx, field_ty)
309 };
310
311 let need_drop = field_ty.needs_drop(tcx, ty_env);
313
314 self.register_place(field_place.clone(), may_drop, need_drop);
315
316 self.create_fields_for_type(
318 tcx,
319 field_ty,
320 field_place,
321 field_depth + 1,
322 deref_depth,
323 ty_env,
324 );
325 }
326 }
327 _ => {
328 }
330 }
331 }
332
333 pub fn register_place(&mut self, place_id: PlaceId, may_drop: bool, need_drop: bool) -> usize {
335 if let Some(&idx) = self.place_to_index.get(&place_id) {
336 return idx;
337 }
338
339 let idx = self.num_places;
340 self.place_to_index.insert(place_id.clone(), idx);
341 self.index_to_place.push(place_id);
342 self.may_drop.push(may_drop);
343 self.need_drop.push(need_drop);
344 self.num_places += 1;
345 idx
346 }
347
348 pub fn get_index(&self, place_id: &PlaceId) -> Option<usize> {
350 self.place_to_index.get(place_id).copied()
351 }
352
353 pub fn get_place(&self, idx: usize) -> Option<&PlaceId> {
355 self.index_to_place.get(idx)
356 }
357
358 pub fn may_drop(&self, idx: usize) -> bool {
360 self.may_drop.get(idx).copied().unwrap_or(false)
361 }
362
363 pub fn need_drop(&self, idx: usize) -> bool {
365 self.need_drop.get(idx).copied().unwrap_or(false)
366 }
367
368 pub fn num_places(&self) -> usize {
370 self.num_places
371 }
372}
373
374#[derive(Clone, PartialEq, Eq, Debug)]
376pub struct AliasDomain {
377 parent: Vec<usize>,
379 rank: Vec<usize>,
381}
382
383impl AliasDomain {
384 pub fn new(num_places: usize) -> Self {
386 AliasDomain {
387 parent: (0..num_places).collect(),
388 rank: vec![0; num_places],
389 }
390 }
391
392 pub fn find(&mut self, idx: usize) -> usize {
394 if self.parent[idx] != idx {
395 self.parent[idx] = self.find(self.parent[idx]);
396 }
397 self.parent[idx]
398 }
399
400 pub fn union(&mut self, idx1: usize, idx2: usize) -> bool {
402 let root1 = self.find(idx1);
403 let root2 = self.find(idx2);
404
405 if root1 == root2 {
406 return false;
407 }
408
409 if self.rank[root1] < self.rank[root2] {
411 self.parent[root1] = root2;
412 } else if self.rank[root1] > self.rank[root2] {
413 self.parent[root2] = root1;
414 } else {
415 self.parent[root2] = root1;
416 self.rank[root1] += 1;
417 }
418
419 true
420 }
421
422 pub fn are_aliased(&mut self, idx1: usize, idx2: usize) -> bool {
424 self.find(idx1) == self.find(idx2)
425 }
426
427 pub fn remove_aliases(&mut self, idx: usize) {
430 let root = self.find(idx);
432
433 let mut component_nodes = Vec::new();
435 for i in 0..self.parent.len() {
436 if self.find(i) == root {
437 component_nodes.push(i);
438 }
439 }
440
441 component_nodes.retain(|&i| i != idx);
443
444 self.parent[idx] = idx;
446 self.rank[idx] = 0;
447
448 if !component_nodes.is_empty() {
450 for &i in &component_nodes {
452 self.parent[i] = i;
453 self.rank[i] = 0;
454 }
455
456 let first = component_nodes[0];
458 for &i in &component_nodes[1..] {
459 self.union(first, i);
460 }
461 }
462 }
463
464 pub fn remove_aliases_with_prefix(&mut self, place_id: &PlaceId, place_info: &PlaceInfo) {
467 let mut indices_to_remove = Vec::new();
469
470 for idx in 0..self.parent.len() {
471 if let Some(pid) = place_info.get_place(idx) {
472 if pid.has_prefix(place_id) {
473 indices_to_remove.push(idx);
474 }
475 }
476 }
477
478 for idx in indices_to_remove {
480 self.remove_aliases(idx);
481 }
482 }
483
484 pub fn get_all_alias_pairs(&self) -> Vec<(usize, usize)> {
486 let mut pairs = Vec::new();
487 let mut domain_clone = self.clone();
488
489 for i in 0..self.parent.len() {
490 for j in (i + 1)..self.parent.len() {
491 if domain_clone.are_aliased(i, j) {
492 pairs.push((i, j));
493 }
494 }
495 }
496
497 pairs
498 }
499}
500
501impl JoinSemiLattice for AliasDomain {
502 fn join(&mut self, other: &Self) -> bool {
503 assert_eq!(
506 self.parent.len(),
507 other.parent.len(),
508 "AliasDomain::join: size mismatch (self: {}, other: {})",
509 self.parent.len(),
510 other.parent.len()
511 );
512
513 let mut changed = false;
514
515 let pairs = other.get_all_alias_pairs();
517 for (i, j) in pairs {
518 if self.union(i, j) {
519 changed = true;
520 }
521 }
522
523 changed
524 }
525}
526
527impl DebugWithContext<FnAliasAnalyzer<'_>> for AliasDomain {}
528
529pub struct FnAliasAnalyzer<'tcx> {
531 pub tcx: TyCtxt<'tcx>,
532 place_info: PlaceInfo<'tcx>,
533 fn_summaries: Rc<RefCell<FnAliasMap>>,
535 pub bb_iter_cnt: RefCell<usize>,
537}
538
539impl<'tcx> FnAliasAnalyzer<'tcx> {
540 pub fn new(
542 tcx: TyCtxt<'tcx>,
543 def_id: DefId,
544 body: &'tcx Body<'tcx>,
545 fn_summaries: Rc<RefCell<FnAliasMap>>,
546 ) -> Self {
547 let place_info = PlaceInfo::build(tcx, def_id, body);
549 FnAliasAnalyzer {
550 tcx,
551 place_info,
552 fn_summaries,
553 bb_iter_cnt: RefCell::new(0),
554 }
555 }
556
557 pub fn place_info(&self) -> &PlaceInfo<'tcx> {
559 &self.place_info
560 }
561}
562
563#[cfg(not(rapx_ge_100))]
567impl<'tcx> Analysis<'tcx> for FnAliasAnalyzer<'tcx> {
568 type Domain = AliasDomain;
569
570 const NAME: &'static str = "FnAliasAnalyzer";
571
572 fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
573 AliasDomain::new(self.place_info.num_places())
574 }
575
576 fn initialize_start_block(&self, _body: &Body<'tcx>, _state: &mut Self::Domain) {}
577
578 fn apply_primary_statement_effect(
579 &self,
580 state: &mut Self::Domain,
581 statement: &Statement<'tcx>,
582 _location: Location,
583 ) {
584 apply_statement_effect(self, state, statement)
585 }
586
587 fn apply_primary_terminator_effect<'mir>(
588 &self,
589 state: &mut Self::Domain,
590 terminator: &'mir Terminator<'tcx>,
591 _location: Location,
592 ) -> TerminatorEdges<'mir, 'tcx> {
593 apply_terminator_effect(self, state, terminator)
594 }
595
596 fn apply_call_return_effect(
597 &self,
598 _state: &mut Self::Domain,
599 _block: rustc_middle::mir::BasicBlock,
600 _return_places: CallReturnPlaces<'_, 'tcx>,
601 ) {
602 }
603}
604
605#[cfg(rapx_ge_100)]
606impl<'tcx> Analysis<'tcx> for FnAliasAnalyzer<'tcx> {
607 type Domain = AliasDomain;
608
609 const NAME: &'static str = "FnAliasAnalyzer";
610
611 fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
612 AliasDomain::new(self.place_info.num_places())
613 }
614
615 fn initialize_start_block(&self, _body: &Body<'tcx>, _state: &mut Self::Domain) {}
616
617 fn apply_primary_statement_effect(
618 &self,
619 state: &mut Self::Domain,
620 statement: &Statement<'tcx>,
621 _location: Location,
622 ) {
623 apply_statement_effect(self, state, statement)
624 }
625
626 fn apply_primary_terminator_effect<'mir>(
627 &self,
628 state: &mut Self::Domain,
629 terminator: &'mir Terminator<'tcx>,
630 _location: Location,
631 ) {
632 apply_terminator_effect(self, state, terminator);
633 }
634
635 fn apply_call_return_effect(
636 &self,
637 _state: &mut Self::Domain,
638 _block: rustc_middle::mir::BasicBlock,
639 _return_places: CallReturnPlaces<'_, 'tcx>,
640 ) {
641 }
642}
643
644fn apply_statement_effect<'tcx>(
645 analyzer: &FnAliasAnalyzer<'tcx>,
646 state: &mut AliasDomain,
647 statement: &Statement<'tcx>,
648) {
649 match &statement.kind {
650 StatementKind::Assign(assign) => {
651 let (lv, rvalue) = &**assign;
652 match rvalue {
653 Rvalue::Use(operand, ..) => {
654 transfer::transfer_assign(state, *lv, operand, &analyzer.place_info);
655 }
656 Rvalue::Ref(_, _, rv) | Rvalue::RawPtr(_, rv) => {
657 transfer::transfer_ref(state, *lv, *rv, &analyzer.place_info);
658 }
659 Rvalue::CopyForDeref(rv) => {
660 transfer::transfer_ref(state, *lv, *rv, &analyzer.place_info);
661 }
662 Rvalue::Cast(_, operand, _) => {
663 transfer::transfer_assign(state, *lv, operand, &analyzer.place_info);
664 }
665 Rvalue::Aggregate(_, operands) => {
666 let operand_slice: Vec<_> = operands.iter().map(|op| op.clone()).collect();
667 transfer::transfer_aggregate(state, *lv, &operand_slice, &analyzer.place_info);
668 }
669 #[cfg(not(rapx_ge_99))]
670 Rvalue::ShallowInitBox(operand, _) => {
671 transfer::transfer_assign(state, *lv, operand, &analyzer.place_info);
672 }
673 _ => {}
674 }
675 }
676 _ => {}
677 }
678}
679
680fn apply_terminator_effect<'tcx, 'mir>(
681 analyzer: &FnAliasAnalyzer<'tcx>,
682 state: &mut AliasDomain,
683 terminator: &'mir Terminator<'tcx>,
684) -> TerminatorEdges<'mir, 'tcx> {
685 {
686 *analyzer.bb_iter_cnt.borrow_mut() += 1;
687 }
688 match &terminator.kind {
689 TerminatorKind::Call {
690 target,
691 destination,
692 args,
693 func,
694 ..
695 } => {
696 let operand_slice: Vec<_> = args
697 .iter()
698 .map(|spanned_arg| spanned_arg.node.clone())
699 .collect();
700 transfer::transfer_call(state, *destination, &operand_slice, &analyzer.place_info);
701
702 if let Operand::Constant(c) = func {
703 if let ty::FnDef(callee_def_id, _) = c.ty().kind() {
704 let fn_summaries = analyzer.fn_summaries.borrow();
705 if let Some(summary) = fn_summaries.get(callee_def_id) {
706 apply_function_summary(
707 state,
708 *destination,
709 &operand_slice,
710 summary,
711 &analyzer.place_info,
712 );
713 } else {
714 drop(fn_summaries);
715 apply_conservative_alias_for_call(
716 state,
717 *destination,
718 args,
719 &analyzer.place_info,
720 );
721 }
722 }
723 }
724
725 if let Some(target_bb) = target {
726 TerminatorEdges::Single(*target_bb)
727 } else {
728 TerminatorEdges::None
729 }
730 }
731
732 TerminatorKind::Drop { target, .. } => TerminatorEdges::Single(*target),
733
734 TerminatorKind::SwitchInt { discr, targets } => {
735 TerminatorEdges::SwitchInt { discr, targets }
736 }
737
738 TerminatorKind::Assert { target, .. } => TerminatorEdges::Single(*target),
739
740 TerminatorKind::Goto { target } => TerminatorEdges::Single(*target),
741
742 TerminatorKind::Return => TerminatorEdges::None,
743
744 _ => TerminatorEdges::None,
745 }
746}