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_analysis::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_rustc_ge_198))]
261 let field_ty = field.ty(tcx, substs);
262 #[cfg(rapx_rustc_ge_198)]
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 _body: &'tcx Body<'tcx>,
533 _def_id: DefId,
534 place_info: PlaceInfo<'tcx>,
535 fn_summaries: Rc<RefCell<FnAliasMap>>,
537 pub bb_iter_cnt: RefCell<usize>,
539}
540
541impl<'tcx> FnAliasAnalyzer<'tcx> {
542 pub fn new(
544 tcx: TyCtxt<'tcx>,
545 def_id: DefId,
546 body: &'tcx Body<'tcx>,
547 fn_summaries: Rc<RefCell<FnAliasMap>>,
548 ) -> Self {
549 let place_info = PlaceInfo::build(tcx, def_id, body);
551 FnAliasAnalyzer {
552 tcx,
553 _body: body,
554 _def_id: def_id,
555 place_info,
556 fn_summaries,
557 bb_iter_cnt: RefCell::new(0),
558 }
559 }
560
561 pub fn place_info(&self) -> &PlaceInfo<'tcx> {
563 &self.place_info
564 }
565}
566
567#[cfg(not(rapx_rustc_ge_193))]
571impl<'tcx> Analysis<'tcx> for FnAliasAnalyzer<'tcx> {
572 type Domain = AliasDomain;
573
574 const NAME: &'static str = "FnAliasAnalyzer";
575
576 fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
577 AliasDomain::new(self.place_info.num_places())
578 }
579
580 fn initialize_start_block(&self, _body: &Body<'tcx>, _state: &mut Self::Domain) {}
581
582 fn apply_primary_statement_effect(
583 &mut self,
584 state: &mut Self::Domain,
585 statement: &Statement<'tcx>,
586 _location: Location,
587 ) {
588 apply_statement_effect(self, state, statement)
589 }
590
591 fn apply_primary_terminator_effect<'mir>(
592 &mut self,
593 state: &mut Self::Domain,
594 terminator: &'mir Terminator<'tcx>,
595 _location: Location,
596 ) -> TerminatorEdges<'mir, 'tcx> {
597 apply_terminator_effect(self, state, terminator)
598 }
599
600 fn apply_call_return_effect(
601 &mut self,
602 _state: &mut Self::Domain,
603 _block: rustc_middle::mir::BasicBlock,
604 _return_places: CallReturnPlaces<'_, 'tcx>,
605 ) {
606 }
607}
608
609#[cfg(rapx_rustc_ge_193)]
610impl<'tcx> Analysis<'tcx> for FnAliasAnalyzer<'tcx> {
611 type Domain = AliasDomain;
612
613 const NAME: &'static str = "FnAliasAnalyzer";
614
615 fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
616 AliasDomain::new(self.place_info.num_places())
617 }
618
619 fn initialize_start_block(&self, _body: &Body<'tcx>, _state: &mut Self::Domain) {}
620
621 fn apply_primary_statement_effect(
622 &self,
623 state: &mut Self::Domain,
624 statement: &Statement<'tcx>,
625 _location: Location,
626 ) {
627 apply_statement_effect(self, state, statement)
628 }
629
630 fn apply_primary_terminator_effect<'mir>(
631 &self,
632 state: &mut Self::Domain,
633 terminator: &'mir Terminator<'tcx>,
634 _location: Location,
635 ) -> TerminatorEdges<'mir, 'tcx> {
636 apply_terminator_effect(self, state, terminator)
637 }
638
639 fn apply_call_return_effect(
640 &self,
641 _state: &mut Self::Domain,
642 _block: rustc_middle::mir::BasicBlock,
643 _return_places: CallReturnPlaces<'_, 'tcx>,
644 ) {
645 }
646}
647
648fn apply_statement_effect<'tcx>(
649 analyzer: &FnAliasAnalyzer<'tcx>,
650 state: &mut AliasDomain,
651 statement: &Statement<'tcx>,
652) {
653 match &statement.kind {
654 StatementKind::Assign(assign) => {
655 let (lv, rvalue) = &**assign;
656 match rvalue {
657 Rvalue::Use(operand, ..) => {
658 transfer::transfer_assign(state, *lv, operand, &analyzer.place_info);
659 }
660 Rvalue::Ref(_, _, rv) | Rvalue::RawPtr(_, rv) => {
661 transfer::transfer_ref(state, *lv, *rv, &analyzer.place_info);
662 }
663 Rvalue::CopyForDeref(rv) => {
664 transfer::transfer_ref(state, *lv, *rv, &analyzer.place_info);
665 }
666 Rvalue::Cast(_, operand, _) => {
667 transfer::transfer_assign(state, *lv, operand, &analyzer.place_info);
668 }
669 Rvalue::Aggregate(_, operands) => {
670 let operand_slice: Vec<_> = operands.iter().map(|op| op.clone()).collect();
671 transfer::transfer_aggregate(state, *lv, &operand_slice, &analyzer.place_info);
672 }
673 #[cfg(not(rapx_rustc_ge_196))]
674 Rvalue::ShallowInitBox(operand, _) => {
675 transfer::transfer_assign(state, *lv, operand, &analyzer.place_info);
676 }
677 _ => {}
678 }
679 }
680 _ => {}
681 }
682}
683
684fn apply_terminator_effect<'tcx, 'mir>(
685 analyzer: &FnAliasAnalyzer<'tcx>,
686 state: &mut AliasDomain,
687 terminator: &'mir Terminator<'tcx>,
688) -> TerminatorEdges<'mir, 'tcx> {
689 {
690 *analyzer.bb_iter_cnt.borrow_mut() += 1;
691 }
692 match &terminator.kind {
693 TerminatorKind::Call {
694 target,
695 destination,
696 args,
697 func,
698 ..
699 } => {
700 let operand_slice: Vec<_> = args
701 .iter()
702 .map(|spanned_arg| spanned_arg.node.clone())
703 .collect();
704 transfer::transfer_call(state, *destination, &operand_slice, &analyzer.place_info);
705
706 if let Operand::Constant(c) = func {
707 if let ty::FnDef(callee_def_id, _) = c.ty().kind() {
708 let fn_summaries = analyzer.fn_summaries.borrow();
709 if let Some(summary) = fn_summaries.get(callee_def_id) {
710 apply_function_summary(
711 state,
712 *destination,
713 &operand_slice,
714 summary,
715 &analyzer.place_info,
716 );
717 } else {
718 drop(fn_summaries);
719 apply_conservative_alias_for_call(
720 state,
721 *destination,
722 args,
723 &analyzer.place_info,
724 );
725 }
726 }
727 }
728
729 if let Some(target_bb) = target {
730 TerminatorEdges::Single(*target_bb)
731 } else {
732 TerminatorEdges::None
733 }
734 }
735
736 TerminatorKind::Drop { target, .. } => TerminatorEdges::Single(*target),
737
738 TerminatorKind::SwitchInt { discr, targets } => {
739 TerminatorEdges::SwitchInt { discr, targets }
740 }
741
742 TerminatorKind::Assert { target, .. } => TerminatorEdges::Single(*target),
743
744 TerminatorKind::Goto { target } => TerminatorEdges::Single(*target),
745
746 TerminatorKind::Return => TerminatorEdges::None,
747
748 _ => TerminatorEdges::None,
749 }
750}