1use crate::{
2 analysis::core::alias_analysis::mop::{types::*, MopAAResult},
3 rap_debug,
4 utils::source::*,
5};
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_middle::{
8 mir::{
9 BasicBlock, Const, Operand, Place, Rvalue, StatementKind, Terminator, TerminatorKind,
10 UnwindAction,
11 },
12 ty::{TyCtxt, TypingEnv},
13};
14use rustc_span::{def_id::DefId, Span};
15use std::{cell::RefCell, cmp::min, vec::Vec};
16
17#[derive(PartialEq, Debug, Copy, Clone)]
18pub enum AssignType {
19 Copy,
20 Move,
21 InitBox,
22 Variant,
23}
24
25#[derive(Debug, Clone)]
27pub struct Assignment<'tcx> {
28 pub lv: Place<'tcx>,
29 pub rv: Place<'tcx>,
30 pub atype: AssignType,
31 pub span: Span,
32}
33
34impl<'tcx> Assignment<'tcx> {
35 pub fn new(
36 lv: Place<'tcx>,
37 rv: Place<'tcx>,
38 atype: AssignType,
39 span: Span,
40 ) -> Assignment<'tcx> {
41 Assignment {
42 lv,
43 rv,
44 atype,
45 span,
46 }
47 }
48}
49
50#[derive(Debug, Clone)]
56pub struct BlockNode<'tcx> {
57 pub index: usize,
58 pub next: FxHashSet<usize>,
59 pub assignments: Vec<Assignment<'tcx>>,
60 pub calls: Vec<Terminator<'tcx>>,
61 pub scc_sub_blocks: Vec<usize>,
63 pub const_value: Vec<(usize, usize)>,
65 pub switch_stmts: Vec<Terminator<'tcx>>,
67 pub modified_value: FxHashSet<usize>,
68 pub scc_outer: RefCell<Option<FxHashMap<(usize, usize), Vec<usize>>>>,
70}
71
72impl<'tcx> BlockNode<'tcx> {
73 pub fn new(index: usize) -> BlockNode<'tcx> {
74 BlockNode {
75 index,
76 next: FxHashSet::<usize>::default(),
77 assignments: Vec::<Assignment<'tcx>>::new(),
78 calls: Vec::<Terminator<'tcx>>::new(),
79 scc_sub_blocks: Vec::<usize>::new(),
80 const_value: Vec::<(usize, usize)>::new(),
81 switch_stmts: Vec::<Terminator<'tcx>>::new(),
82 modified_value: FxHashSet::<usize>::default(),
83 scc_outer: RefCell::new(None),
84 }
85 }
86
87 pub fn add_next(&mut self, index: usize) {
88 self.next.insert(index);
89 }
90}
91
92#[derive(Debug, Clone)]
93pub struct ValueNode {
94 pub index: usize, pub local: usize, pub need_drop: bool,
97 pub may_drop: bool,
98 pub kind: TyKind,
99 pub father: usize,
100 pub field_id: usize, pub fields: FxHashMap<usize, usize>,
102}
103
104impl ValueNode {
105 pub fn new(index: usize, local: usize, need_drop: bool, may_drop: bool) -> Self {
106 ValueNode {
107 index,
108 local,
109 need_drop,
110 father: local,
111 field_id: usize::MAX,
112 may_drop,
113 kind: TyKind::Adt,
114 fields: FxHashMap::default(),
115 }
116 }
117
118 pub fn is_tuple(&self) -> bool {
119 self.kind == TyKind::Tuple
120 }
121
122 pub fn is_ptr(&self) -> bool {
123 self.kind == TyKind::RawPtr || self.kind == TyKind::Ref
124 }
125
126 pub fn is_ref(&self) -> bool {
127 self.kind == TyKind::Ref
128 }
129
130 pub fn is_corner_case(&self) -> bool {
131 self.kind == TyKind::CornerCase
132 }
133}
134
135pub struct MopGraph<'tcx> {
136 pub def_id: DefId,
137 pub tcx: TyCtxt<'tcx>,
138 pub span: Span,
139 pub values: Vec<ValueNode>,
141 pub blocks: Vec<BlockNode<'tcx>>,
143 pub arg_size: usize,
144 pub scc_indices: Vec<usize>,
146 pub constant: FxHashMap<usize, usize>,
148 pub ret_alias: MopAAResult,
150 pub visit_times: usize,
152 pub alias_set: Vec<usize>,
153 pub child_scc: FxHashMap<
154 usize,
155 (
156 BlockNode<'tcx>,
157 rustc_middle::mir::SwitchTargets,
158 FxHashSet<usize>,
159 ),
160 >,
161 pub disc_map: FxHashMap<usize, usize>,
162 pub terms: Vec<TerminatorKind<'tcx>>,
163}
164
165impl<'tcx> MopGraph<'tcx> {
166 pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId) -> MopGraph<'tcx> {
167 let fn_name = get_fn_name(tcx, def_id);
168 rap_debug!("Building a new MoP graph for: {:?}", fn_name);
169 let body = tcx.optimized_mir(def_id);
171 let locals = &body.local_decls;
172 let arg_size = body.arg_count;
173 let mut values = Vec::<ValueNode>::new();
174 let mut alias = Vec::<usize>::new();
175 let ty_env = TypingEnv::post_analysis(tcx, def_id);
176 for (local, local_decl) in locals.iter_enumerated() {
177 let need_drop = local_decl.ty.needs_drop(tcx, ty_env); let may_drop = !is_not_drop(tcx, local_decl.ty);
179 let mut node = ValueNode::new(
180 local.as_usize(),
181 local.as_usize(),
182 need_drop,
183 need_drop || may_drop,
184 );
185 node.kind = kind(local_decl.ty);
186 alias.push(values.len());
187 values.push(node);
188 }
189
190 let basicblocks = &body.basic_blocks;
191 let mut blocks = Vec::<BlockNode<'tcx>>::new();
192 let mut scc_indices = Vec::<usize>::new();
193 let mut disc_map = FxHashMap::default();
194 let mut terms = Vec::new();
195
196 for i in 0..basicblocks.len() {
198 scc_indices.push(i);
199 let iter = BasicBlock::from(i);
200 let terminator = basicblocks[iter].terminator.clone().unwrap();
201 let mut cur_bb = BlockNode::new(i);
202
203 for stmt in &basicblocks[iter].statements {
205 let span = stmt.source_info.span;
207 if let StatementKind::Assign(ref assign) = stmt.kind {
208 let lv_local = assign.0.local.as_usize(); let lv = assign.0;
210 cur_bb.modified_value.insert(lv_local);
211 match assign.1 {
212 Rvalue::Use(ref x) => {
214 match x {
215 Operand::Copy(ref p) => {
216 let rv_local = p.local.as_usize();
217 if values[lv_local].may_drop && values[rv_local].may_drop {
218 let rv = *p;
219 let assign =
220 Assignment::new(lv, rv, AssignType::Copy, span);
221 cur_bb.assignments.push(assign);
222 }
223 }
224 Operand::Move(ref p) => {
225 let rv_local = p.local.as_usize();
226 if values[lv_local].may_drop && values[rv_local].may_drop {
227 let rv = *p;
228 let assign =
229 Assignment::new(lv, rv, AssignType::Move, span);
230 cur_bb.assignments.push(assign);
231 }
232 }
233 Operand::Constant(ref constant) => {
234 match constant.const_ {
238 Const::Ty(_ty, const_value) => {
239 if let Some(val) = const_value.try_to_target_usize(tcx)
240 {
241 cur_bb.const_value.push((lv_local, val as usize));
242 }
243 }
244 Const::Unevaluated(_const_value, _ty) => {}
245 Const::Val(const_value, _ty) => {
246 if let Some(scalar) = const_value.try_to_scalar_int() {
248 let val = scalar.to_uint(scalar.size());
249 cur_bb.const_value.push((lv_local, val as usize));
250 }
251 }
252 }
253 }
254 }
255 }
256 Rvalue::Ref(_, _, ref p)
257 | Rvalue::RawPtr(_, ref p)
258 | Rvalue::CopyForDeref(ref p) => {
259 let rv_local = p.local.as_usize();
260 if values[lv_local].may_drop && values[rv_local].may_drop {
261 let rv = *p;
262 let assign = Assignment::new(lv, rv, AssignType::Copy, span);
263 cur_bb.assignments.push(assign);
264 }
265 }
266 Rvalue::ShallowInitBox(ref x, _) => {
267 if !values[lv_local].fields.contains_key(&0) {
273 let mut lvl0 = ValueNode::new(values.len(), lv_local, false, true);
274 lvl0.field_id = 0;
275 values[lv_local].fields.insert(0, lvl0.index);
276 alias.push(values.len());
277 values.push(lvl0);
278 }
279 match x {
280 Operand::Copy(ref p) | Operand::Move(ref p) => {
281 let rv_local = p.local.as_usize();
282 if values[lv_local].may_drop && values[rv_local].may_drop {
283 let rv = *p;
284 let assign =
285 Assignment::new(lv, rv, AssignType::InitBox, span);
286 cur_bb.assignments.push(assign);
287 }
288 }
289 Operand::Constant(_) => {}
290 }
291 }
292 Rvalue::Cast(_, ref x, _) => match x {
293 Operand::Copy(ref p) => {
294 let rv_local = p.local.as_usize();
295 if values[lv_local].may_drop && values[rv_local].may_drop {
296 let rv = *p;
297 let assign = Assignment::new(lv, rv, AssignType::Copy, span);
298 cur_bb.assignments.push(assign);
299 }
300 }
301 Operand::Move(ref p) => {
302 let rv_local = p.local.as_usize();
303 if values[lv_local].may_drop && values[rv_local].may_drop {
304 let rv = *p;
305 let assign = Assignment::new(lv, rv, AssignType::Move, span);
306 cur_bb.assignments.push(assign);
307 }
308 }
309 Operand::Constant(_) => {}
310 },
311 Rvalue::Aggregate(_, ref x) => {
312 for each_x in x {
313 match each_x {
314 Operand::Copy(ref p) | Operand::Move(ref p) => {
315 let rv_local = p.local.as_usize();
316 if values[lv_local].may_drop && values[rv_local].may_drop {
317 let rv = *p;
318 let assign =
319 Assignment::new(lv, rv, AssignType::Copy, span);
320 cur_bb.assignments.push(assign);
321 }
322 }
323 Operand::Constant(_) => {}
324 }
325 }
326 }
327 Rvalue::Discriminant(ref p) => {
328 let rv = *p;
329 let assign = Assignment::new(lv, rv, AssignType::Variant, span);
330 cur_bb.assignments.push(assign);
331 disc_map.insert(lv_local, p.local.as_usize());
332 }
333 _ => {}
334 }
335 }
336 }
337
338 terms.push(terminator.kind.clone());
339 match terminator.kind {
341 TerminatorKind::Goto { ref target } => {
342 cur_bb.add_next(target.as_usize());
343 }
344 TerminatorKind::SwitchInt {
345 discr: _,
346 ref targets,
347 } => {
348 cur_bb.switch_stmts.push(terminator.clone());
349 for (_, ref target) in targets.iter() {
350 cur_bb.add_next(target.as_usize());
351 }
352 cur_bb.add_next(targets.otherwise().as_usize());
353 }
354 TerminatorKind::UnwindResume
355 | TerminatorKind::Return
356 | TerminatorKind::UnwindTerminate(_)
357 | TerminatorKind::CoroutineDrop
358 | TerminatorKind::Unreachable => {}
359 TerminatorKind::Drop {
360 place: _,
361 ref target,
362 ref unwind,
363 replace: _,
364 drop: _,
365 async_fut: _,
366 } => {
367 cur_bb.add_next(target.as_usize());
368 if let UnwindAction::Cleanup(target) = unwind {
369 cur_bb.add_next(target.as_usize());
370 }
371 }
372 TerminatorKind::Call {
373 func: _,
374 args: _,
375 destination: _,
376 ref target,
377 ref unwind,
378 call_source: _,
379 fn_span: _,
380 } => {
381 if let Some(tt) = target {
382 cur_bb.add_next(tt.as_usize());
383 }
384 if let UnwindAction::Cleanup(tt) = unwind {
385 cur_bb.add_next(tt.as_usize());
386 }
387 cur_bb.calls.push(terminator.clone());
388 }
389 TerminatorKind::TailCall { .. } => todo!(),
390 TerminatorKind::Assert {
391 cond: _,
392 expected: _,
393 msg: _,
394 ref target,
395 ref unwind,
396 } => {
397 cur_bb.add_next(target.as_usize());
398 if let UnwindAction::Cleanup(target) = unwind {
399 cur_bb.add_next(target.as_usize());
400 }
401 }
402 TerminatorKind::Yield {
403 value: _,
404 ref resume,
405 resume_arg: _,
406 ref drop,
407 } => {
408 cur_bb.add_next(resume.as_usize());
409 if let Some(target) = drop {
410 cur_bb.add_next(target.as_usize());
411 }
412 }
413 TerminatorKind::FalseEdge {
414 ref real_target,
415 imaginary_target: _,
416 } => {
417 cur_bb.add_next(real_target.as_usize());
418 }
419 TerminatorKind::FalseUnwind {
420 ref real_target,
421 unwind: _,
422 } => {
423 cur_bb.add_next(real_target.as_usize());
424 }
425 TerminatorKind::InlineAsm {
426 template: _,
427 operands: _,
428 options: _,
429 line_spans: _,
430 ref unwind,
431 targets,
432 asm_macro: _,
433 } => {
434 for target in targets {
435 cur_bb.add_next(target.as_usize());
436 }
437 if let UnwindAction::Cleanup(target) = unwind {
438 cur_bb.add_next(target.as_usize());
439 }
440 }
441 }
442 blocks.push(cur_bb);
443 }
444
445 MopGraph {
446 def_id,
447 tcx,
448 span: body.span,
449 blocks,
450 values,
451 arg_size,
452 scc_indices,
453 alias_set: alias,
454 constant: FxHashMap::default(),
455 ret_alias: MopAAResult::new(arg_size),
456 visit_times: 0,
457 child_scc: FxHashMap::default(),
458 disc_map,
459 terms,
460 }
461 }
462
463 pub fn tarjan(
464 &mut self,
465 index: usize,
466 stack: &mut Vec<usize>,
467 instack: &mut FxHashSet<usize>,
468 dfn: &mut Vec<usize>,
469 low: &mut Vec<usize>,
470 time: &mut usize,
471 ) {
472 dfn[index] = *time;
473 low[index] = *time;
474 *time += 1;
475 instack.insert(index);
476 stack.push(index);
477 let out_set = self.blocks[index].next.clone();
478 for target in out_set {
479 if dfn[target] == 0 {
480 self.tarjan(target, stack, instack, dfn, low, time);
481 low[index] = min(low[index], low[target]);
482 } else if instack.contains(&target) {
483 low[index] = min(low[index], dfn[target]);
484 }
485 }
486 if dfn[index] == low[index] {
488 let mut modified_set = FxHashSet::<usize>::default();
489 let mut switch_target = Vec::new();
490 let mut scc_block_set = FxHashSet::<usize>::default();
491 let init_block = self.blocks[index].clone();
492 loop {
493 let node = stack.pop().unwrap();
494 self.scc_indices[node] = index;
495 instack.remove(&node);
496 if index == node {
497 break;
499 }
500 self.blocks[index].scc_sub_blocks.push(node);
501 scc_block_set.insert(node);
502
503 for value in &self.blocks[index].modified_value {
504 modified_set.insert(*value);
505 }
506 if let Some(target) = self.switch_target(node) {
507 if !self.blocks[index].switch_stmts.is_empty() {
508 switch_target.push((target, self.blocks[index].switch_stmts[0].clone()));
509 }
510 }
511
512 let nexts = self.blocks[node].next.clone();
513 for i in nexts {
514 self.blocks[index].next.insert(i);
515 }
516 }
517 switch_target.retain(|v| !modified_set.contains(&(v.0)));
518
519 if !switch_target.is_empty() && switch_target.len() == 1 {
520 let target_terminator = switch_target[0].1.clone();
522
523 let TerminatorKind::SwitchInt { discr: _, targets } = target_terminator.kind else {
524 unreachable!();
525 };
526
527 self.child_scc
528 .insert(index, (init_block, targets, scc_block_set));
529 }
530 let mut to_remove = Vec::new();
532 for i in self.blocks[index].next.iter() {
533 if self.scc_indices[*i] == index {
534 to_remove.push(*i);
535 }
536 }
537 for i in to_remove {
538 self.blocks[index].next.remove(&i);
539 }
540 self.blocks[index].scc_sub_blocks.reverse();
545 }
546 }
547
548 pub fn solve_scc(&mut self) {
550 let mut stack = Vec::<usize>::new();
551 let mut instack = FxHashSet::<usize>::default();
552 let mut dfn = vec![0_usize; self.blocks.len()];
553 let mut low = vec![0_usize; self.blocks.len()];
554 let mut time = 0;
555 self.tarjan(0, &mut stack, &mut instack, &mut dfn, &mut low, &mut time);
556 }
557
558 pub fn switch_target(&mut self, block_index: usize) -> Option<usize> {
559 let block = &self.blocks[block_index];
560 if block.switch_stmts.is_empty() {
561 return None;
562 }
563
564 let res = if let TerminatorKind::SwitchInt {
565 ref discr,
566 targets: _,
567 } = &block.switch_stmts[0].kind
568 {
569 match discr {
570 Operand::Copy(p) | Operand::Move(p) => {
571 let place = self.projection(false, p.clone());
572 Some(place)
573 }
574 _ => None,
575 }
576 } else {
577 None
578 };
579
580 res
581 }
582}