rapx/analysis/core/alias_analysis/default/
graph.rs

1use super::{MopFnAliasPairs, assign::*, block::*, types::*, value::*};
2use crate::{
3    def_id::*,
4    utils::{
5        scc::{Scc, SccExit},
6        source::*,
7    },
8};
9use rustc_data_structures::fx::{FxHashMap, FxHashSet};
10use rustc_middle::{
11    mir::{
12        AggregateKind, BasicBlock, Const, Operand, Rvalue, StatementKind, TerminatorKind,
13        UnwindAction,
14    },
15    ty::{self, TyCtxt, TypingEnv},
16};
17use rustc_span::{Span, def_id::DefId};
18use std::{
19    fmt::{self, Display},
20    vec::Vec,
21};
22
23#[derive(Clone)]
24pub struct MopGraph<'tcx> {
25    pub def_id: DefId,
26    pub tcx: TyCtxt<'tcx>,
27    // The field is used to detect dangling pointers in arguments after the function returns.
28    pub arg_size: usize,
29    pub span: Span,
30    // All values (including fields) of the function.
31    // For general variables, we use its Local as the value index;
32    // For fields, the value index is determined via auto increment.
33    pub values: Vec<Value>,
34    // All blocks of the function;
35    // Each block is initialized as a basic block of the mir;
36    // The blocks are then aggregated into strongly-connected components.
37    pub blocks: Vec<Block<'tcx>>,
38    // We record the constant value for path sensitivity.
39    pub constants: FxHashMap<usize, usize>,
40    // We record the decision of enumerate typed values for path sensitivity.
41    pub discriminants: FxHashMap<usize, usize>,
42    // a threhold to avoid path explosion.
43    pub visit_times: usize,
44    pub alias_sets: Vec<FxHashSet<usize>>,
45    // contains the return results for inter-procedure analysis.
46    pub ret_alias: MopFnAliasPairs,
47    pub terminators: Vec<TerminatorKind<'tcx>>,
48}
49
50impl<'tcx> MopGraph<'tcx> {
51    pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId) -> MopGraph<'tcx> {
52        let fn_name = get_fn_name(tcx, def_id);
53        rap_debug!("New a MopGraph for: {:?}", fn_name);
54        // handle variables
55        let body = tcx.optimized_mir(def_id);
56        //display_mir(def_id, body);
57        let locals = &body.local_decls;
58        let arg_size = body.arg_count;
59        let mut values = Vec::<Value>::new();
60        let ty_env = TypingEnv::post_analysis(tcx, def_id);
61        for (local, local_decl) in locals.iter_enumerated() {
62            let need_drop = local_decl.ty.needs_drop(tcx, ty_env); // the type is drop
63            let may_drop = !is_not_drop(tcx, local_decl.ty);
64            let mut node = Value::new(
65                local.as_usize(),
66                local.as_usize(),
67                need_drop,
68                need_drop || may_drop,
69            );
70            node.kind = kind(local_decl.ty);
71            values.push(node);
72        }
73
74        let basicblocks = &body.basic_blocks;
75        let mut blocks = Vec::<Block<'tcx>>::new();
76        let mut discriminants = FxHashMap::default();
77        let mut terminators = Vec::new();
78
79        // handle each basicblock
80        for i in 0..basicblocks.len() {
81            let bb = &basicblocks[BasicBlock::from(i)];
82            let mut cur_bb = Block::new(i, bb.is_cleanup);
83
84            // handle general statements
85            for stmt in &bb.statements {
86                let span = stmt.source_info.span;
87                match &stmt.kind {
88                    StatementKind::Assign(box (place, rvalue)) => {
89                        let lv_place = *place;
90                        let lv_local = place.local.as_usize();
91                        cur_bb.assigned_locals.insert(lv_local);
92                        match rvalue.clone() {
93                            // rvalue is a Rvalue
94                            Rvalue::Use(operand) => {
95                                match operand {
96                                    Operand::Copy(rv_place) => {
97                                        let rv_local = rv_place.local.as_usize();
98                                        if values[lv_local].may_drop && values[rv_local].may_drop {
99                                            let assign = Assignment::new(
100                                                lv_place,
101                                                rv_place,
102                                                AssignType::Copy,
103                                                span,
104                                            );
105                                            cur_bb.assignments.push(assign);
106                                        }
107                                    }
108                                    Operand::Move(rv_place) => {
109                                        let rv_local = rv_place.local.as_usize();
110                                        if values[lv_local].may_drop && values[rv_local].may_drop {
111                                            let assign = Assignment::new(
112                                                lv_place,
113                                                rv_place,
114                                                AssignType::Move,
115                                                span,
116                                            );
117                                            cur_bb.assignments.push(assign);
118                                        }
119                                    }
120                                    Operand::Constant(constant) => {
121                                        /* We should check the correctness due to the update of rustc
122                                         * https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.Const.html
123                                         */
124                                        match constant.const_ {
125                                            Const::Ty(_ty, const_value) => {
126                                                if let Some(val) =
127                                                    const_value.try_to_target_usize(tcx)
128                                                {
129                                                    cur_bb.const_value.push(ConstValue::new(
130                                                        lv_local,
131                                                        val as usize,
132                                                    ));
133                                                }
134                                            }
135                                            Const::Unevaluated(_const_value, _ty) => {}
136                                            Const::Val(const_value, _ty) => {
137                                                if let Some(scalar) =
138                                                    const_value.try_to_scalar_int()
139                                                {
140                                                    let val = scalar.to_uint(scalar.size());
141                                                    cur_bb.const_value.push(ConstValue::new(
142                                                        lv_local,
143                                                        val as usize,
144                                                    ));
145                                                }
146                                            }
147                                        }
148                                    }
149                                }
150                            }
151                            Rvalue::Ref(_, _, rv_place)
152                            | Rvalue::RawPtr(_, rv_place)
153                            | Rvalue::CopyForDeref(rv_place) => {
154                                let rv_local = rv_place.local.as_usize();
155                                if values[lv_local].may_drop && values[rv_local].may_drop {
156                                    let assign =
157                                        Assignment::new(lv_place, rv_place, AssignType::Copy, span);
158                                    cur_bb.assignments.push(assign);
159                                }
160                            }
161                            Rvalue::ShallowInitBox(operand, _) => {
162                                /*
163                                 * Original ShllowInitBox is a two-level pointer: lvl0 -> lvl1 -> lvl2
164                                 * Since our alias analysis does not consider multi-level pointer,
165                                 * We simplify it as: lvl0
166                                 */
167                                if !values[lv_local].fields.contains_key(&0) {
168                                    let mut lvl0 = Value::new(values.len(), lv_local, false, true);
169                                    lvl0.father = Some(FatherInfo::new(lv_local, 0));
170                                    values[lv_local].fields.insert(0, lvl0.index);
171                                    values.push(lvl0);
172                                }
173                                match operand {
174                                    Operand::Copy(rv_place) | Operand::Move(rv_place) => {
175                                        let rv_local = rv_place.local.as_usize();
176                                        if values[lv_local].may_drop && values[rv_local].may_drop {
177                                            let assign = Assignment::new(
178                                                lv_place,
179                                                rv_place,
180                                                AssignType::InitBox,
181                                                span,
182                                            );
183                                            cur_bb.assignments.push(assign);
184                                        }
185                                    }
186                                    Operand::Constant(_) => {}
187                                }
188                            }
189                            Rvalue::Cast(_, operand, _) => match operand {
190                                Operand::Copy(rv_place) => {
191                                    let rv_local = rv_place.local.as_usize();
192                                    if values[lv_local].may_drop && values[rv_local].may_drop {
193                                        let assign = Assignment::new(
194                                            lv_place,
195                                            rv_place,
196                                            AssignType::Copy,
197                                            span,
198                                        );
199                                        cur_bb.assignments.push(assign);
200                                    }
201                                }
202                                Operand::Move(rv_place) => {
203                                    let rv_local = rv_place.local.as_usize();
204                                    if values[lv_local].may_drop && values[rv_local].may_drop {
205                                        let assign = Assignment::new(
206                                            lv_place,
207                                            rv_place,
208                                            AssignType::Move,
209                                            span,
210                                        );
211                                        cur_bb.assignments.push(assign);
212                                    }
213                                }
214                                Operand::Constant(_) => {}
215                            },
216                            Rvalue::Aggregate(kind, operands) => {
217                                match kind.as_ref() {
218                                    // For tuple aggregation such as _10 = (move _11, move _12)
219                                    // we create `_10.0 = move _11` and `_10.1 = move _12` to achieve field sensitivity
220                                    // and prevent transitive alias: (_10, _11) + (_10, _12) => (_11, _12)
221                                    AggregateKind::Tuple => {
222                                        let lv_ty = lv_place.ty(&body.local_decls, tcx).ty;
223                                        for (field_idx, operand) in operands.iter_enumerated() {
224                                            match operand {
225                                                Operand::Copy(rv_place)
226                                                | Operand::Move(rv_place) => {
227                                                    let rv_local = rv_place.local.as_usize();
228                                                    if values[lv_local].may_drop
229                                                        && values[rv_local].may_drop
230                                                    {
231                                                        // Get field type from tuple or array
232                                                        let field_ty = match lv_ty.kind() {
233                                                            ty::Tuple(fields) => {
234                                                                fields[field_idx.as_usize()]
235                                                            }
236                                                            _ => {
237                                                                continue;
238                                                            }
239                                                        };
240
241                                                        // Create lv.field_idx Place using tcx.mk_place_field
242                                                        let lv_field_place = tcx.mk_place_field(
243                                                            lv_place, field_idx, field_ty,
244                                                        );
245
246                                                        let assign = Assignment::new(
247                                                            lv_field_place,
248                                                            *rv_place,
249                                                            if matches!(operand, Operand::Move(_)) {
250                                                                AssignType::Move
251                                                            } else {
252                                                                AssignType::Copy
253                                                            },
254                                                            span,
255                                                        );
256                                                        cur_bb.assignments.push(assign);
257                                                        rap_debug!(
258                                                            "Aggregate field assignment: {:?}.{} = {:?}",
259                                                            lv_place,
260                                                            field_idx.as_usize(),
261                                                            rv_place
262                                                        );
263                                                    }
264                                                }
265                                                Operand::Constant(_) => {
266                                                    // Constants don't need alias analysis
267                                                }
268                                            }
269                                        }
270                                    }
271                                    AggregateKind::Adt(_, _, _, _, _) => {
272                                        // For ADTs (structs/enums), handle field assignments field-sensitively.
273                                        // NOTE: Here we treat the ADT similarly to tuples,
274                                        // but fields might be named and ADT type info is available, so more precise field indexing is possible if needed.
275                                        let lv_ty = lv_place.ty(&body.local_decls, tcx).ty;
276                                        for (field_idx, operand) in operands.iter_enumerated() {
277                                            match operand {
278                                                Operand::Copy(rv_place)
279                                                | Operand::Move(rv_place) => {
280                                                    let rv_local = rv_place.local.as_usize();
281                                                    if values[lv_local].may_drop
282                                                        && values[rv_local].may_drop
283                                                    {
284                                                        // If possible, resolve field type for better analysis. Here we use tuple logic as a template.
285                                                        let field_ty = match lv_ty.kind() {
286                                                            ty::Adt(adt_def, substs) => {
287                                                                // Try getting the field type if available.
288                                                                if field_idx.as_usize()
289                                                                    < adt_def.all_fields().count()
290                                                                {
291                                                                    adt_def
292                                                                        .all_fields()
293                                                                        .nth(field_idx.as_usize())
294                                                                        .map(|f| f.ty(tcx, substs))
295                                                                        .unwrap_or(lv_ty) // fallback
296                                                                } else {
297                                                                    lv_ty
298                                                                }
299                                                            }
300                                                            _ => lv_ty,
301                                                        };
302
303                                                        // Create lv.field_idx Place using tcx.mk_place_field, as for tuples.
304                                                        let lv_field_place = tcx.mk_place_field(
305                                                            lv_place, field_idx, field_ty,
306                                                        );
307
308                                                        let assign = Assignment::new(
309                                                            lv_field_place,
310                                                            *rv_place,
311                                                            if matches!(operand, Operand::Move(_)) {
312                                                                AssignType::Move
313                                                            } else {
314                                                                AssignType::Copy
315                                                            },
316                                                            span,
317                                                        );
318                                                        cur_bb.assignments.push(assign);
319                                                        rap_debug!(
320                                                            "Aggregate ADT field assignment: {:?}.{} = {:?}",
321                                                            lv_place,
322                                                            field_idx.as_usize(),
323                                                            rv_place
324                                                        );
325                                                    }
326                                                }
327                                                Operand::Constant(_) => {
328                                                    // Constants don't need alias analysis for this context.
329                                                }
330                                            }
331                                        }
332                                    }
333                                    // TODO: Support alias for array
334                                    AggregateKind::Array(_) => {}
335                                    // For other aggregate types, simply create an assignment for each aggregated operand
336                                    _ => {
337                                        for operand in operands {
338                                            match operand {
339                                                Operand::Copy(rv_place)
340                                                | Operand::Move(rv_place) => {
341                                                    let rv_local = rv_place.local.as_usize();
342                                                    if values[lv_local].may_drop
343                                                        && values[rv_local].may_drop
344                                                    {
345                                                        let assign = Assignment::new(
346                                                            lv_place,
347                                                            rv_place,
348                                                            AssignType::Copy,
349                                                            span,
350                                                        );
351                                                        cur_bb.assignments.push(assign);
352                                                    }
353                                                }
354                                                Operand::Constant(_) => {}
355                                            }
356                                        }
357                                    }
358                                }
359                            }
360                            Rvalue::Discriminant(rv_place) => {
361                                let assign =
362                                    Assignment::new(lv_place, rv_place, AssignType::Variant, span);
363                                cur_bb.assignments.push(assign);
364                                discriminants.insert(lv_local, rv_place.local.as_usize());
365                            }
366                            _ => {}
367                        }
368                    }
369                    StatementKind::SetDiscriminant {
370                        place: _,
371                        variant_index: _,
372                    } => {
373                        rap_warn!("SetDiscriminant: {:?} is not handled in RAPx!", stmt);
374                    }
375                    _ => {}
376                }
377            }
378
379            let Some(terminator) = &bb.terminator else {
380                rap_info!(
381                    "Basic block BB{} has no terminator in function {:?}",
382                    i,
383                    fn_name
384                );
385                continue;
386            };
387            terminators.push(terminator.kind.clone());
388            // handle terminator statements
389            match terminator.kind.clone() {
390                TerminatorKind::Goto { ref target } => {
391                    cur_bb.add_next(target.as_usize());
392                }
393                TerminatorKind::SwitchInt {
394                    discr: _,
395                    ref targets,
396                } => {
397                    cur_bb.terminator = Term::Switch(terminator.clone());
398                    for (_, ref target) in targets.iter() {
399                        cur_bb.add_next(target.as_usize());
400                    }
401                    cur_bb.add_next(targets.otherwise().as_usize());
402                }
403                TerminatorKind::Drop {
404                    place: _,
405                    target,
406                    unwind,
407                    replace: _,
408                    drop: _,
409                    async_fut: _,
410                } => {
411                    cur_bb.add_next(target.as_usize());
412                    cur_bb.terminator = Term::Drop(terminator.clone());
413                    if let UnwindAction::Cleanup(target) = unwind {
414                        cur_bb.add_next(target.as_usize());
415                    }
416                }
417                TerminatorKind::Call {
418                    ref func,
419                    args: _,
420                    destination: _,
421                    ref target,
422                    ref unwind,
423                    call_source: _,
424                    fn_span: _,
425                } => {
426                    if let Operand::Constant(c) = func {
427                        if let &ty::FnDef(id, ..) = c.ty().kind() {
428                            if is_drop_fn(id) {
429                                cur_bb.terminator = Term::Drop(terminator.clone());
430                            } else {
431                                cur_bb.terminator = Term::Call(terminator.clone());
432                            }
433                        }
434                    } else {
435                        cur_bb.terminator = Term::Call(terminator.clone());
436                    }
437                    if let Some(tt) = target {
438                        cur_bb.add_next(tt.as_usize());
439                    }
440                    if let UnwindAction::Cleanup(tt) = unwind {
441                        cur_bb.add_next(tt.as_usize());
442                    }
443                }
444
445                TerminatorKind::Assert {
446                    cond: _,
447                    expected: _,
448                    msg: _,
449                    ref target,
450                    ref unwind,
451                } => {
452                    cur_bb.add_next(target.as_usize());
453                    if let UnwindAction::Cleanup(target) = unwind {
454                        cur_bb.add_next(target.as_usize());
455                    }
456                }
457                TerminatorKind::Yield {
458                    value: _,
459                    ref resume,
460                    resume_arg: _,
461                    ref drop,
462                } => {
463                    cur_bb.add_next(resume.as_usize());
464                    if let Some(target) = drop {
465                        cur_bb.add_next(target.as_usize());
466                    }
467                }
468                TerminatorKind::FalseEdge {
469                    ref real_target,
470                    imaginary_target: _,
471                } => {
472                    cur_bb.add_next(real_target.as_usize());
473                }
474                TerminatorKind::FalseUnwind {
475                    ref real_target,
476                    unwind: _,
477                } => {
478                    cur_bb.add_next(real_target.as_usize());
479                }
480                TerminatorKind::InlineAsm {
481                    template: _,
482                    operands: _,
483                    options: _,
484                    line_spans: _,
485                    ref unwind,
486                    targets,
487                    asm_macro: _,
488                } => {
489                    for target in targets {
490                        cur_bb.add_next(target.as_usize());
491                    }
492                    if let UnwindAction::Cleanup(target) = unwind {
493                        cur_bb.add_next(target.as_usize());
494                    }
495                }
496                _ => {}
497            }
498            blocks.push(cur_bb);
499        }
500
501        MopGraph {
502            def_id,
503            tcx,
504            span: body.span,
505            blocks,
506            values,
507            arg_size,
508            alias_sets: Vec::<FxHashSet<usize>>::new(),
509            constants: FxHashMap::default(),
510            ret_alias: MopFnAliasPairs::new(arg_size),
511            visit_times: 0,
512            discriminants,
513            terminators,
514        }
515    }
516
517    /// Enumerate acyclic CFG paths from the current block to an exit block.
518    ///
519    /// MIR loops are represented as back edges in `Block::next`. The path
520    /// consumer only needs one finite traversal that reaches each unsafe
521    /// callsite, so this DFS cuts a path when it would revisit a block already
522    /// on the current stack. Non-cycle successors are still explored, which
523    /// keeps loop exits visible without risking unbounded path growth.
524    pub fn dfs_on_spanning_tree(
525        &self,
526        index: usize,
527        stack: &mut Vec<usize>,
528        paths: &mut Vec<Vec<usize>>,
529    ) {
530        const PATH_ENUM_LIMIT: usize = 4000;
531
532        if paths.len() >= PATH_ENUM_LIMIT {
533            return;
534        }
535        if index >= self.blocks.len() {
536            return;
537        }
538
539        let mut nexts: Vec<usize> = self.blocks[index].next.iter().copied().collect();
540        nexts.sort_unstable();
541
542        if nexts.is_empty() {
543            paths.push(stack.clone());
544            return;
545        }
546
547        let mut followed = false;
548        for next in nexts {
549            if paths.len() >= PATH_ENUM_LIMIT {
550                break;
551            }
552            if next >= self.blocks.len() {
553                continue;
554            }
555            if stack.contains(&next) {
556                paths.push(stack.clone());
557                continue;
558            }
559
560            followed = true;
561            stack.push(next);
562            self.dfs_on_spanning_tree(next, stack, paths);
563            stack.pop();
564        }
565
566        if !followed {
567            paths.push(stack.clone());
568        }
569    }
570
571    /// Return all finite MIR CFG paths starting from the entry block.
572    pub fn get_paths(&self) -> Vec<Vec<usize>> {
573        let mut paths: Vec<Vec<usize>> = Vec::new();
574        if self.blocks.is_empty() {
575            return paths;
576        }
577        let mut stack: Vec<usize> = vec![0];
578        self.dfs_on_spanning_tree(0, &mut stack, &mut paths);
579        paths
580    }
581    pub fn get_all_branch_sub_blocks_paths(&self) -> Vec<Vec<usize>> {
582        // 1. Get all execution paths
583        let all_paths = self.get_paths();
584
585        // Use FxHashSet to collect all unique lists of dominated_scc_bbs.
586        // Vec<usize> implements Hash, so it can be inserted directly into the set.
587        let mut unique_branch_sub_blocks = FxHashSet::<Vec<usize>>::default();
588
589        // 2. Iterate over each path
590        for path in all_paths {
591            // 3. For the current path, get the corresponding dominated_scc_bbs for branch nodes
592            let branch_blocks_for_this_path = self.get_branch_sub_blocks_for_path(&path);
593            rap_debug!(
594                "Branch blocks for path {:?}: {:?}",
595                path,
596                branch_blocks_for_this_path
597            );
598            // 4. Add these dominated_scc_bbs to the set
599            //    Use insert to avoid duplicates
600            unique_branch_sub_blocks.insert(branch_blocks_for_this_path);
601        }
602
603        // 5. Convert the set of unique results back to a Vec and return
604        unique_branch_sub_blocks.into_iter().collect()
605    }
606
607    pub fn get_branch_sub_blocks_for_path(&self, path: &[usize]) -> Vec<usize> {
608        let mut expanded_path = Vec::new();
609        // Use FxHashSet to track which SCCs have already been expanded in this path,
610        // preventing repeated expansion due to cycles.
611        let mut processed_scc_indices = FxHashSet::default();
612
613        for &block_idx in path {
614            // 1. Get the representative SCC index of the current basic block
615            let scc_idx = self.blocks[block_idx].scc.enter;
616
617            // 2. Try inserting scc_idx into the set. If successful (returns true),
618            // it means this SCC is encountered for the first time.
619            if processed_scc_indices.insert(scc_idx) {
620                // First time encountering this SCC: perform full expansion
621
622                // a. First, add the SCC representative itself to the path
623                expanded_path.push(scc_idx);
624
625                // b. Then, retrieve the SCC node information
626                let scc_enter = &self.blocks[scc_idx];
627
628                // c. If it has sub-blocks (i.e., it’s a multi-node SCC),
629                // append all sub-blocks to the path.
630                // dominated_scc_bbs are already ordered (topologically or near-topologically)
631                if !scc_enter.scc.nodes.is_empty() {
632                    //expanded_path.extend_from_slice(&scc_enter.scc.nodes);
633                }
634            } else {
635                // SCC already seen before (e.g., due to a cycle in the path):
636                // Only add the representative node as a connector, skip internal blocks.
637                expanded_path.push(scc_idx);
638            }
639        }
640
641        expanded_path
642    }
643}
644
645pub trait SccHelper<'tcx> {
646    fn tcx(&self) -> TyCtxt<'tcx>;
647    fn defid(&self) -> DefId;
648    fn blocks(&self) -> &Vec<Block<'tcx>>; // or whatever the actual type is
649    fn blocks_mut(&mut self) -> &mut Vec<Block<'tcx>>;
650}
651
652impl<'tcx> SccHelper<'tcx> for MopGraph<'tcx> {
653    fn tcx(&self) -> TyCtxt<'tcx> {
654        self.tcx
655    }
656    fn defid(&self) -> DefId {
657        self.def_id
658    }
659    fn blocks(&self) -> &Vec<Block<'tcx>> {
660        &self.blocks
661    }
662    fn blocks_mut(&mut self) -> &mut Vec<Block<'tcx>> {
663        &mut self.blocks
664    }
665}
666
667pub fn scc_handler<'tcx, T: SccHelper<'tcx> + Scc + Clone + Display>(
668    graph: &mut T,
669    root: usize,
670    scc_components: &[usize],
671) {
672    rap_debug!(
673        "Scc found: root = {}, components = {:?}",
674        root,
675        scc_components
676    );
677    graph.blocks_mut()[root].scc.enter = root;
678    if scc_components.len() <= 1 {
679        return;
680    }
681
682    // If the scc enter is also an exit of the scc; add it to the scc exit;
683    let nexts = graph.blocks_mut()[root].next.clone();
684    for next in nexts {
685        if !&scc_components.contains(&next) {
686            let scc_exit = SccExit::new(root, next);
687            graph.blocks_mut()[root].scc.exits.insert(scc_exit);
688        }
689    }
690    // Handle other nodes of the scc;
691    for &node in &scc_components[1..] {
692        graph.blocks_mut()[root].scc.nodes.insert(node);
693        graph.blocks_mut()[node].scc.enter = root;
694        let nexts = graph.blocks_mut()[node].next.clone();
695        for next in nexts {
696            // The node is an scc exit.
697            if !&scc_components.contains(&next) {
698                let scc_exit = SccExit::new(node, next);
699                graph.blocks_mut()[root].scc.exits.insert(scc_exit);
700            }
701            // The node initiates a back edge to the scc enter.
702            if next == root {
703                graph.blocks_mut()[root].scc.backnodes.insert(node);
704            }
705        }
706    }
707
708    rap_debug!("Scc Info: {:?}", graph.blocks_mut()[root].scc);
709    // Recursively detect sub sccs within the scc.
710    // This is performed on a modified graph with the starting node and scc components only;
711    // Before modification, we have to backup corresponding information.
712    let mut backups: Vec<(usize, FxHashSet<usize>)> = Vec::new();
713
714    let block0 = &mut graph.blocks_mut()[0];
715    backups.push((0, block0.next.clone()));
716
717    // Change the next of block0 to the scc enter.
718    block0.next.clear();
719    block0.next.insert(root);
720
721    let scc_exits = graph.blocks()[root].scc.exits.clone();
722    let backnodes = graph.blocks()[root].scc.backnodes.clone();
723
724    for &node in scc_components.iter() {
725        let block = &mut graph.blocks_mut()[node];
726        if backnodes.contains(&node) {
727            backups.push((node, block.next.clone()));
728            block.next.remove(&root);
729        }
730    }
731    // isolate the scc components from other parts of the graph.
732    for exit in &scc_exits {
733        let block_to = &mut graph.blocks_mut()[exit.to];
734        backups.push((exit.to, block_to.next.clone()));
735        block_to.next.clear();
736    }
737    graph.find_scc();
738
739    // Restore the original graph.
740    for backup in &backups {
741        graph.blocks_mut()[backup.0].next = backup.1.clone();
742    }
743}
744
745impl<'tcx> Scc for MopGraph<'tcx> {
746    fn on_scc_found(&mut self, root: usize, scc_components: &[usize]) {
747        scc_handler(self, root, scc_components);
748    }
749    fn get_next(&mut self, root: usize) -> FxHashSet<usize> {
750        self.blocks[root].next.clone()
751    }
752    fn get_size(&mut self) -> usize {
753        self.blocks.len()
754    }
755}
756
757// Implement Display for debugging / printing purposes.
758// Prints selected fields: def_id, values, blocks, constants, discriminants, scc_indices, child_scc.
759impl<'tcx> std::fmt::Display for MopGraph<'tcx> {
760    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761        writeln!(f, "MopGraph {{")?;
762        writeln!(f, "  def_id: {:?}", self.def_id)?;
763        writeln!(f, "  values: {:?}", self.values)?;
764        writeln!(f, "  blocks: {:?}", self.blocks)?;
765        writeln!(f, "  constants: {:?}", self.constants)?;
766        writeln!(f, "  discriminants: {:?}", self.discriminants)?;
767        writeln!(f, "  terminators: {:?}", self.terminators)?;
768        write!(f, "}}")
769    }
770}