1extern crate rustc_mir_dataflow;
2use rustc_data_structures::fx::FxHashMap;
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::core::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: &[rustc_span::source_map::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 place_to_mir: FxHashMap<PlaceId, Place<'tcx>>,
175 may_drop: Vec<bool>,
177 need_drop: Vec<bool>,
179 num_places: usize,
181}
182
183impl<'tcx> PlaceInfo<'tcx> {
184 pub fn new() -> Self {
186 PlaceInfo {
187 place_to_index: FxHashMap::default(),
188 index_to_place: Vec::new(),
189 place_to_mir: FxHashMap::default(),
190 may_drop: Vec::new(),
191 need_drop: Vec::new(),
192 num_places: 0,
193 }
194 }
195
196 pub fn build(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) -> Self {
198 let mut info = Self::new();
199 let ty_env = TypingEnv::post_analysis(tcx, def_id);
200
201 for (local, local_decl) in body.local_decls.iter_enumerated() {
203 let ty = local_decl.ty;
204 let need_drop = ty.needs_drop(tcx, ty_env);
205 let may_drop = !is_not_drop(tcx, ty);
206
207 let place_id = PlaceId::Local(local.as_usize());
208 info.register_place(place_id.clone(), may_drop, need_drop);
209
210 info.create_fields_for_type(tcx, ty, place_id, 0, 0, ty_env);
212 }
213
214 info
215 }
216
217 fn create_fields_for_type(
219 &mut self,
220 tcx: TyCtxt<'tcx>,
221 ty: Ty<'tcx>,
222 base_place: PlaceId,
223 field_depth: usize,
224 deref_depth: usize,
225 ty_env: TypingEnv<'tcx>,
226 ) {
227 const MAX_FIELD_DEPTH: usize = 5;
229 const MAX_DEREF_DEPTH: usize = 3;
230 if field_depth >= MAX_FIELD_DEPTH || deref_depth >= MAX_DEREF_DEPTH {
231 return;
232 }
233
234 match ty.kind() {
235 ty::Ref(_, inner_ty, _) => {
238 self.create_fields_for_type(
239 tcx,
240 *inner_ty,
241 base_place,
242 field_depth,
243 deref_depth + 1,
244 ty_env,
245 );
246 }
247 ty::RawPtr(inner_ty, _) => {
249 self.create_fields_for_type(
250 tcx,
251 *inner_ty,
252 base_place,
253 field_depth,
254 deref_depth + 1,
255 ty_env,
256 );
257 }
258 ty::Adt(adt_def, substs) => {
260 for (field_idx, field) in adt_def.all_fields().enumerate() {
261 let field_ty = field.ty(tcx, substs);
262 let field_place = base_place.project_field(field_idx);
263
264 let need_drop = field_ty.needs_drop(tcx, ty_env);
267
268 let may_drop = if deref_depth > 0 {
274 true
275 } else {
276 !is_not_drop(tcx, field_ty)
277 };
278
279 self.register_place(field_place.clone(), may_drop, need_drop);
280
281 self.create_fields_for_type(
283 tcx,
284 field_ty,
285 field_place,
286 field_depth + 1,
287 deref_depth,
288 ty_env,
289 );
290 }
291 }
292 ty::Tuple(fields) => {
294 for (field_idx, field_ty) in fields.iter().enumerate() {
295 let field_place = base_place.project_field(field_idx);
296
297 let may_drop = if deref_depth > 0 {
304 true
305 } else {
306 !is_not_drop(tcx, field_ty)
307 };
308
309 let need_drop = field_ty.needs_drop(tcx, ty_env);
311
312 self.register_place(field_place.clone(), may_drop, need_drop);
313
314 self.create_fields_for_type(
316 tcx,
317 field_ty,
318 field_place,
319 field_depth + 1,
320 deref_depth,
321 ty_env,
322 );
323 }
324 }
325 _ => {
326 }
328 }
329 }
330
331 pub fn register_place(&mut self, place_id: PlaceId, may_drop: bool, need_drop: bool) -> usize {
333 if let Some(&idx) = self.place_to_index.get(&place_id) {
334 return idx;
335 }
336
337 let idx = self.num_places;
338 self.place_to_index.insert(place_id.clone(), idx);
339 self.index_to_place.push(place_id);
340 self.may_drop.push(may_drop);
341 self.need_drop.push(need_drop);
342 self.num_places += 1;
343 idx
344 }
345
346 pub fn get_index(&self, place_id: &PlaceId) -> Option<usize> {
348 self.place_to_index.get(place_id).copied()
349 }
350
351 pub fn get_place(&self, idx: usize) -> Option<&PlaceId> {
353 self.index_to_place.get(idx)
354 }
355
356 pub fn may_drop(&self, idx: usize) -> bool {
358 self.may_drop.get(idx).copied().unwrap_or(false)
359 }
360
361 pub fn need_drop(&self, idx: usize) -> bool {
363 self.need_drop.get(idx).copied().unwrap_or(false)
364 }
365
366 pub fn num_places(&self) -> usize {
368 self.num_places
369 }
370
371 pub fn associate_mir_place(&mut self, place_id: PlaceId, mir_place: Place<'tcx>) {
373 self.place_to_mir.insert(place_id, mir_place);
374 }
375}
376
377#[derive(Clone, PartialEq, Eq, Debug)]
379pub struct AliasDomain {
380 parent: Vec<usize>,
382 rank: Vec<usize>,
384}
385
386impl AliasDomain {
387 pub fn new(num_places: usize) -> Self {
389 AliasDomain {
390 parent: (0..num_places).collect(),
391 rank: vec![0; num_places],
392 }
393 }
394
395 pub fn find(&mut self, idx: usize) -> usize {
397 if self.parent[idx] != idx {
398 self.parent[idx] = self.find(self.parent[idx]);
399 }
400 self.parent[idx]
401 }
402
403 pub fn union(&mut self, idx1: usize, idx2: usize) -> bool {
405 let root1 = self.find(idx1);
406 let root2 = self.find(idx2);
407
408 if root1 == root2 {
409 return false;
410 }
411
412 if self.rank[root1] < self.rank[root2] {
414 self.parent[root1] = root2;
415 } else if self.rank[root1] > self.rank[root2] {
416 self.parent[root2] = root1;
417 } else {
418 self.parent[root2] = root1;
419 self.rank[root1] += 1;
420 }
421
422 true
423 }
424
425 pub fn are_aliased(&mut self, idx1: usize, idx2: usize) -> bool {
427 self.find(idx1) == self.find(idx2)
428 }
429
430 pub fn remove_aliases(&mut self, idx: usize) {
433 let root = self.find(idx);
435
436 let mut component_nodes = Vec::new();
438 for i in 0..self.parent.len() {
439 if self.find(i) == root {
440 component_nodes.push(i);
441 }
442 }
443
444 component_nodes.retain(|&i| i != idx);
446
447 self.parent[idx] = idx;
449 self.rank[idx] = 0;
450
451 if !component_nodes.is_empty() {
453 for &i in &component_nodes {
455 self.parent[i] = i;
456 self.rank[i] = 0;
457 }
458
459 let first = component_nodes[0];
461 for &i in &component_nodes[1..] {
462 self.union(first, i);
463 }
464 }
465 }
466
467 pub fn remove_aliases_with_prefix(&mut self, place_id: &PlaceId, place_info: &PlaceInfo) {
470 let mut indices_to_remove = Vec::new();
472
473 for idx in 0..self.parent.len() {
474 if let Some(pid) = place_info.get_place(idx) {
475 if pid.has_prefix(place_id) {
476 indices_to_remove.push(idx);
477 }
478 }
479 }
480
481 for idx in indices_to_remove {
483 self.remove_aliases(idx);
484 }
485 }
486
487 pub fn get_all_alias_pairs(&self) -> Vec<(usize, usize)> {
489 let mut pairs = Vec::new();
490 let mut domain_clone = self.clone();
491
492 for i in 0..self.parent.len() {
493 for j in (i + 1)..self.parent.len() {
494 if domain_clone.are_aliased(i, j) {
495 pairs.push((i, j));
496 }
497 }
498 }
499
500 pairs
501 }
502}
503
504impl JoinSemiLattice for AliasDomain {
505 fn join(&mut self, other: &Self) -> bool {
506 assert_eq!(
509 self.parent.len(),
510 other.parent.len(),
511 "AliasDomain::join: size mismatch (self: {}, other: {})",
512 self.parent.len(),
513 other.parent.len()
514 );
515
516 let mut changed = false;
517
518 let pairs = other.get_all_alias_pairs();
520 for (i, j) in pairs {
521 if self.union(i, j) {
522 changed = true;
523 }
524 }
525
526 changed
527 }
528}
529
530impl DebugWithContext<FnAliasAnalyzer<'_>> for AliasDomain {}
531
532pub struct FnAliasAnalyzer<'tcx> {
534 pub tcx: TyCtxt<'tcx>,
535 _body: &'tcx Body<'tcx>,
536 _def_id: DefId,
537 place_info: PlaceInfo<'tcx>,
538 fn_summaries: Rc<RefCell<FnAliasMap>>,
540 pub bb_iter_cnt: RefCell<usize>,
542}
543
544impl<'tcx> FnAliasAnalyzer<'tcx> {
545 pub fn new(
547 tcx: TyCtxt<'tcx>,
548 def_id: DefId,
549 body: &'tcx Body<'tcx>,
550 fn_summaries: Rc<RefCell<FnAliasMap>>,
551 ) -> Self {
552 let place_info = PlaceInfo::build(tcx, def_id, body);
554 FnAliasAnalyzer {
555 tcx,
556 _body: body,
557 _def_id: def_id,
558 place_info,
559 fn_summaries,
560 bb_iter_cnt: RefCell::new(0),
561 }
562 }
563
564 pub fn place_info(&self) -> &PlaceInfo<'tcx> {
566 &self.place_info
567 }
568}
569
570impl<'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())
579 }
580
581 fn initialize_start_block(&self, _body: &Body<'tcx>, _state: &mut Self::Domain) {
582 }
584
585 fn apply_primary_statement_effect(
586 &self,
587 state: &mut Self::Domain,
588 statement: &Statement<'tcx>,
589 _location: Location,
590 ) {
591 match &statement.kind {
592 StatementKind::Assign(box (lv, rvalue)) => {
593 match rvalue {
594 Rvalue::Use(operand) => {
596 transfer::transfer_assign(state, *lv, operand, &self.place_info);
597 }
598 Rvalue::Ref(_, _, rv) | Rvalue::RawPtr(_, rv) => {
600 transfer::transfer_ref(state, *lv, *rv, &self.place_info);
601 }
602 Rvalue::CopyForDeref(rv) => {
604 transfer::transfer_ref(state, *lv, *rv, &self.place_info);
605 }
606 Rvalue::Cast(_, operand, _) => {
608 transfer::transfer_assign(state, *lv, operand, &self.place_info);
609 }
610 Rvalue::Aggregate(_, operands) => {
612 let operand_slice: Vec<_> = operands.iter().map(|op| op.clone()).collect();
613 transfer::transfer_aggregate(state, *lv, &operand_slice, &self.place_info);
614 }
615 Rvalue::ShallowInitBox(operand, _) => {
617 transfer::transfer_assign(state, *lv, operand, &self.place_info);
618 }
619 _ => {}
621 }
622 }
623 _ => {}
625 }
626 }
627
628 fn apply_primary_terminator_effect<'mir>(
629 &self,
630 state: &mut Self::Domain,
631 terminator: &'mir Terminator<'tcx>,
632 _location: Location,
633 ) -> TerminatorEdges<'mir, 'tcx> {
634 {
636 *self.bb_iter_cnt.borrow_mut() += 1;
637 }
638 match &terminator.kind {
639 TerminatorKind::Call {
644 target,
645 destination,
646 args,
647 func,
648 ..
649 } => {
650 let operand_slice: Vec<_> = args
652 .iter()
653 .map(|spanned_arg| spanned_arg.node.clone())
654 .collect();
655 transfer::transfer_call(state, *destination, &operand_slice, &self.place_info);
656
657 if let Operand::Constant(c) = func {
659 if let ty::FnDef(callee_def_id, _) = c.ty().kind() {
660 let fn_summaries = self.fn_summaries.borrow();
662 if let Some(summary) = fn_summaries.get(callee_def_id) {
663 apply_function_summary(
665 state,
666 *destination,
667 &operand_slice,
668 summary,
669 &self.place_info,
670 );
671 } else {
672 drop(fn_summaries);
675
676 apply_conservative_alias_for_call(
678 state,
679 *destination,
680 args,
681 &self.place_info,
682 );
683 }
684 } else {
685 }
691 }
692
693 if let Some(target_bb) = target {
695 TerminatorEdges::Single(*target_bb)
696 } else {
697 TerminatorEdges::None
698 }
699 }
700
701 TerminatorKind::Drop { target, .. } => TerminatorEdges::Single(*target),
703
704 TerminatorKind::SwitchInt { discr, targets } => {
706 TerminatorEdges::SwitchInt { discr, targets }
707 }
708
709 TerminatorKind::Assert { target, .. } => TerminatorEdges::Single(*target),
711
712 TerminatorKind::Goto { target } => TerminatorEdges::Single(*target),
714
715 TerminatorKind::Return => TerminatorEdges::None,
717
718 _ => TerminatorEdges::None,
720 }
721 }
722
723 fn apply_call_return_effect(
724 &self,
725 _state: &mut Self::Domain,
726 _block: rustc_middle::mir::BasicBlock,
727 _return_places: CallReturnPlaces<'_, 'tcx>,
728 ) {
729 }
735}