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

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