Skip to content
Snippets Groups Projects
pass.rs 32.34 KiB
extern crate hercules_cg;
extern crate hercules_ir;
extern crate postcard;
extern crate serde;
extern crate take_mut;

use std::cell::RefCell;
use std::collections::HashMap;
use std::env::temp_dir;
use std::fs::File;
use std::io::Write;
use std::iter::zip;
use std::process::{Command, Stdio};

use self::serde::Deserialize;

use self::hercules_cg::*;
use self::hercules_ir::*;

use crate::*;

/*
 * Passes that can be run on a module.
 */
#[derive(Debug, Clone, Deserialize)]
pub enum Pass {
    DCE,
    CCP,
    GVN,
    Forkify,
    PhiElim,
    ForkGuardElim,
    Predication,
    SROA,
    Inline,
    Verify,
    // Parameterized over whether analyses that aid visualization are necessary.
    // Useful to set to false if displaying a potentially broken module.
    Xdot(bool),
    SchedXdot,
    // Parameterized over output directory and module name.
    Codegen(String, String),
    // Parameterized over where to serialize module to.
    Serialize(String),
    InterproceduralSROA,
}

/*
 * Manages passes to be run on an IR module. Transparently handles analysis
 * requirements for optimizations.
 */
#[derive(Debug, Clone)]
pub struct PassManager {
    module: Module,

    // Passes to run.
    passes: Vec<Pass>,

    // Cached analysis results.
    pub def_uses: Option<Vec<ImmutableDefUseMap>>,
    pub reverse_postorders: Option<Vec<Vec<NodeID>>>,
    pub typing: Option<ModuleTyping>,
    pub control_subgraphs: Option<Vec<Subgraph>>,
    pub doms: Option<Vec<DomTree>>,
    pub postdoms: Option<Vec<DomTree>>,
    pub fork_join_maps: Option<Vec<HashMap<NodeID, NodeID>>>,
    pub fork_join_nests: Option<Vec<HashMap<NodeID, Vec<NodeID>>>>,
    pub loops: Option<Vec<LoopTree>>,
    pub antideps: Option<Vec<Vec<(NodeID, NodeID)>>>,
    pub bbs: Option<Vec<Vec<NodeID>>>,
    pub callgraph: Option<CallGraph>,

    // Current plan.
    pub plans: Option<Vec<Plan>>,

    // Store the manifest of a compiled object.
    pub manifests: Option<HashMap<String, Manifest>>,
}

impl PassManager {
    pub fn new(module: Module) -> Self {
        PassManager {
            module,
            passes: vec![],
            def_uses: None,
            reverse_postorders: None,
            typing: None,
            control_subgraphs: None,
            doms: None,
            postdoms: None,
            fork_join_maps: None,
            fork_join_nests: None,
            loops: None,
            antideps: None,
            bbs: None,
            callgraph: None,
            plans: None,
            manifests: None,
        }
    }

    pub fn add_pass(&mut self, pass: Pass) {
        self.passes.push(pass);
    }

    pub fn make_def_uses(&mut self) {
        if self.def_uses.is_none() {
            self.def_uses = Some(self.module.functions.iter().map(def_use).collect());
        }
    }

    pub fn make_reverse_postorders(&mut self) {
        if self.reverse_postorders.is_none() {
            self.make_def_uses();
            self.reverse_postorders = Some(
                self.def_uses
                    .as_ref()
                    .unwrap()
                    .iter()
                    .map(reverse_postorder)
                    .collect(),
            );
        }
    }

    pub fn make_typing(&mut self) {
        if self.typing.is_none() {
            self.make_reverse_postorders();
            self.typing = Some(
                typecheck(&mut self.module, self.reverse_postorders.as_ref().unwrap()).unwrap(),
            );
        }
    }

    pub fn make_control_subgraphs(&mut self) {
        if self.control_subgraphs.is_none() {
            self.make_def_uses();
            self.control_subgraphs = Some(
                zip(&self.module.functions, self.def_uses.as_ref().unwrap())
                    .map(|(function, def_use)| control_subgraph(function, def_use))
                    .collect(),
            );
        }
    }

    pub fn make_doms(&mut self) {
        if self.doms.is_none() {
            self.make_control_subgraphs();
            self.doms = Some(
                self.control_subgraphs
                    .as_ref()
                    .unwrap()
                    .iter()
                    .map(|subgraph| dominator(subgraph, NodeID::new(0)))
                    .collect(),
            );
        }
    }

    pub fn make_postdoms(&mut self) {
        if self.postdoms.is_none() {
            self.make_control_subgraphs();
            self.postdoms = Some(
                zip(
                    self.control_subgraphs.as_ref().unwrap().iter(),
                    self.module.functions.iter(),
                )
                .map(|(subgraph, function)| dominator(subgraph, NodeID::new(function.nodes.len())))
                .collect(),
            );
        }
    }

    pub fn make_fork_join_maps(&mut self) {
        if self.fork_join_maps.is_none() {
            self.make_control_subgraphs();
            self.fork_join_maps = Some(
                zip(
                    self.module.functions.iter(),
                    self.control_subgraphs.as_ref().unwrap().iter(),
                )
                .map(|(function, subgraph)| fork_join_map(function, subgraph))
                .collect(),
            );
        }
    }

    pub fn make_fork_join_nests(&mut self) {
        if self.fork_join_nests.is_none() {
            self.make_doms();
            self.make_fork_join_maps();
            self.fork_join_nests = Some(
                zip(
                    self.module.functions.iter(),
                    zip(
                        self.doms.as_ref().unwrap().iter(),
                        self.fork_join_maps.as_ref().unwrap().iter(),
                    ),
                )
                .map(|(function, (dom, fork_join_map))| {
                    compute_fork_join_nesting(function, dom, fork_join_map)
                })
                .collect(),
            );
        }
    }

    pub fn make_loops(&mut self) {
        if self.loops.is_none() {
            self.make_control_subgraphs();
            self.make_doms();
            self.make_fork_join_maps();
            let control_subgraphs = self.control_subgraphs.as_ref().unwrap().iter();
            let doms = self.doms.as_ref().unwrap().iter();
            let fork_join_maps = self.fork_join_maps.as_ref().unwrap().iter();
            self.loops = Some(
                zip(control_subgraphs, zip(doms, fork_join_maps))
                    .map(|(control_subgraph, (dom, fork_join_map))| {
                        loops(control_subgraph, NodeID::new(0), dom, fork_join_map)
                    })
                    .collect(),
            );
        }
    }

    pub fn make_antideps(&mut self) {
        if self.antideps.is_none() {
            self.make_def_uses();
            self.antideps = Some(
                zip(
                    self.def_uses.as_ref().unwrap().iter(),
                    self.module.functions.iter(),
                )
                .map(|(def_use, function)| antideps(function, def_use))
                .collect(),
            );
        }
    }

    pub fn make_bbs(&mut self) {
        if self.bbs.is_none() {
            self.make_def_uses();
            self.make_reverse_postorders();
            self.make_doms();
            self.make_antideps();
            self.make_loops();
            self.make_fork_join_maps();
            let def_uses = self.def_uses.as_ref().unwrap().iter();
            let reverse_postorders = self.reverse_postorders.as_ref().unwrap().iter();
            let doms = self.doms.as_ref().unwrap().iter();
            let antideps = self.antideps.as_ref().unwrap().iter();
            let loops = self.loops.as_ref().unwrap().iter();
            let fork_join_maps = self.fork_join_maps.as_ref().unwrap().iter();
            self.bbs = Some(
                zip(
                    self.module.functions.iter(),
                    zip(
                        def_uses,
                        zip(
                            reverse_postorders,
                            zip(doms, zip(antideps, zip(loops, fork_join_maps))),
                        ),
                    ),
                )
                .map(
                    |(
                        function,
                        (def_use, (reverse_postorder, (dom, (antideps, (loops, fork_join_map))))),
                    )| {
                        gcm(
                            function,
                            def_use,
                            reverse_postorder,
                            dom,
                            antideps,
                            loops,
                            fork_join_map,
                            &mut vec![None; function.nodes.len()],
                        )
                    },
                )
                .collect(),
            );
        }
    }

    pub fn make_callgraph(&mut self) {
        if self.callgraph.is_none() {
            self.callgraph = Some(callgraph(&self.module));
        }
    }

    pub fn set_plans(&mut self, plans: Vec<Plan>) {
        self.plans = Some(plans);
    }

    pub fn make_plans(&mut self) {
        if self.plans.is_none() {
            self.make_def_uses();
            self.make_reverse_postorders();
            self.make_fork_join_maps();
            self.make_fork_join_nests();
            self.make_bbs();
            let def_uses = self.def_uses.as_ref().unwrap().iter();
            let reverse_postorders = self.reverse_postorders.as_ref().unwrap().iter();
            let fork_join_maps = self.fork_join_maps.as_ref().unwrap().iter();
            let fork_join_nests = self.fork_join_nests.as_ref().unwrap().iter();
            let bbs = self.bbs.as_ref().unwrap().iter();
            self.plans = Some(
                zip(
                    self.module.functions.iter(),
                    zip(
                        def_uses,
                        zip(
                            reverse_postorders,
                            zip(fork_join_maps, zip(fork_join_nests, bbs)),
                        ),
                    ),
                )
                .map(
                    |(
                        function,
                        (def_use, (reverse_postorder, (fork_join_map, (fork_join_nest, bb)))),
                    )| {
                        default_plan(
                            function,
                            &self.module.dynamic_constants,
                            def_use,
                            reverse_postorder,
                            fork_join_map,
                            fork_join_nest,
                            bb,
                        )
                    },
                )
                .collect(),
            );
        }
    }

    pub fn run_passes(&mut self) {
        for pass in self.passes.clone().iter() {
            match pass {
                Pass::DCE => {
                    self.make_def_uses();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        let constants_ref =
                            RefCell::new(std::mem::take(&mut self.module.constants));
                        let dynamic_constants_ref =
                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
                        let mut editor = FunctionEditor::new(
                            &mut self.module.functions[idx],
                            &constants_ref,
                            &dynamic_constants_ref,
                            &types_ref,
                            &def_uses[idx],
                        );
                        dce(&mut editor);

                        self.module.constants = constants_ref.take();
                        self.module.dynamic_constants = dynamic_constants_ref.take();
                        self.module.types = types_ref.take();

                        let edits = &editor.edits();
                        if let Some(plans) = self.plans.as_mut() {
                            repair_plan(&mut plans[idx], &self.module.functions[idx], edits);
                        }
                        let grave_mapping = self.module.functions[idx].delete_gravestones();
                        if let Some(plans) = self.plans.as_mut() {
                            plans[idx].fix_gravestones(&grave_mapping);
                        }
                    }
                    self.clear_analyses();
                }
                Pass::InterproceduralSROA => {
                    self.make_def_uses();
                    let mut plans = self.plans.as_mut();

                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
                    let dynamic_constants_ref =
                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));

                    let def_uses = self.def_uses.as_ref().unwrap();

                    let mut editors: Vec<_> = self
                        .module
                        .functions
                        .iter_mut()
                        .enumerate()
                        .map(|(i, f)| {
                            FunctionEditor::new(
                                f,
                                &constants_ref,
                                &dynamic_constants_ref,
                                &types_ref,
                                &def_uses[i],
                            )
                        })
                        .collect();

                    interprocedural_sroa(&mut editors);

                    let function_edits: Vec<_> =
                        editors.into_iter().map(|e| e.edits()).enumerate().collect();

                    self.module.constants = constants_ref.take();
                    self.module.dynamic_constants = dynamic_constants_ref.take();
                    self.module.types = types_ref.take();

                    for (idx, edits) in function_edits {
                        if let Some(plans) = plans.as_mut() {
                            repair_plan(&mut plans[idx], &self.module.functions[idx], &edits);
                        }
                        let grave_mapping = &self.module.functions[idx].delete_gravestones();
                        if let Some(plans) = plans.as_mut() {
                            plans[idx].fix_gravestones(&grave_mapping);
                        }
                    }

                    self.clear_analyses();
                }
                Pass::CCP => {
                    self.make_def_uses();
                    self.make_reverse_postorders();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        ccp(
                            &mut self.module.functions[idx],
                            &mut self.module.constants,
                            &def_uses[idx],
                            &reverse_postorders[idx],
                        );
                    }
                    self.legacy_repair_plan();
                    self.clear_analyses();
                }
                Pass::GVN => {
                    self.make_def_uses();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        let constants_ref =
                            RefCell::new(std::mem::take(&mut self.module.constants));
                        let dynamic_constants_ref =
                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
                        let mut editor = FunctionEditor::new(
                            &mut self.module.functions[idx],
                            &constants_ref,
                            &dynamic_constants_ref,
                            &types_ref,
                            &def_uses[idx],
                        );
                        gvn(&mut editor);

                        self.module.constants = constants_ref.take();
                        self.module.dynamic_constants = dynamic_constants_ref.take();
                        self.module.types = types_ref.take();

                        let edits = &editor.edits();
                        if let Some(plans) = self.plans.as_mut() {
                            repair_plan(&mut plans[idx], &self.module.functions[idx], edits);
                        }
                        let grave_mapping = self.module.functions[idx].delete_gravestones();
                        if let Some(plans) = self.plans.as_mut() {
                            plans[idx].fix_gravestones(&grave_mapping);
                        }
                    }
                    self.clear_analyses();
                }
                Pass::Forkify => {
                    self.make_def_uses();
                    self.make_loops();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    let loops = self.loops.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        forkify(
                            &mut self.module.functions[idx],
                            &self.module.constants,
                            &mut self.module.dynamic_constants,
                            &def_uses[idx],
                            &loops[idx],
                        )
                    }
                    self.legacy_repair_plan();
                    self.clear_analyses();
                }
                Pass::PhiElim => {
                    for function in self.module.functions.iter_mut() {
                        phi_elim(function);
                    }
                    self.legacy_repair_plan();
                    self.clear_analyses();
                }
                Pass::ForkGuardElim => {
                    self.make_def_uses();
                    self.make_fork_join_maps();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        fork_guard_elim(
                            &mut self.module.functions[idx],
                            &self.module.constants,
                            &fork_join_maps[idx],
                            &def_uses[idx],
                        )
                    }
                    self.legacy_repair_plan();
                    self.clear_analyses();
                }
                Pass::Predication => {
                    self.make_def_uses();
                    self.make_reverse_postorders();
                    self.make_doms();
                    self.make_fork_join_maps();
                    self.make_plans();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
                    let doms = self.doms.as_ref().unwrap();
                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
                    let plans = self.plans.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        predication(
                            &mut self.module.functions[idx],
                            &def_uses[idx],
                            &reverse_postorders[idx],
                            &doms[idx],
                            &fork_join_maps[idx],
                            &plans[idx].schedules,
                        )
                    }
                    self.legacy_repair_plan();
                    self.clear_analyses();
                }
                Pass::SROA => {
                    self.make_def_uses();
                    self.make_reverse_postorders();
                    self.make_typing();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
                    let typing = self.typing.as_ref().unwrap();
                    for idx in 0..self.module.functions.len() {
                        let constants_ref =
                            RefCell::new(std::mem::take(&mut self.module.constants));
                        let dynamic_constants_ref =
                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
                        let mut editor = FunctionEditor::new(
                            &mut self.module.functions[idx],
                            &constants_ref,
                            &dynamic_constants_ref,
                            &types_ref,
                            &def_uses[idx],
                        );
                        sroa(&mut editor, &reverse_postorders[idx], &typing[idx]);

                        self.module.constants = constants_ref.take();
                        self.module.dynamic_constants = dynamic_constants_ref.take();
                        self.module.types = types_ref.take();

                        let edits = &editor.edits();
                        if let Some(plans) = self.plans.as_mut() {
                            repair_plan(&mut plans[idx], &self.module.functions[idx], edits);
                        }
                        let grave_mapping = self.module.functions[idx].delete_gravestones();
                        if let Some(plans) = self.plans.as_mut() {
                            plans[idx].fix_gravestones(&grave_mapping);
                        }
                    }
                    self.clear_analyses();
                }
                Pass::Inline => {
                    self.make_def_uses();
                    self.make_callgraph();
                    let def_uses = self.def_uses.as_ref().unwrap();
                    let callgraph = self.callgraph.as_ref().unwrap();
                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
                    let dynamic_constants_ref =
                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
                    let mut editors: Vec<_> =
                        zip(self.module.functions.iter_mut(), def_uses.iter())
                            .map(|(func, def_use)| {
                                FunctionEditor::new(
                                    func,
                                    &constants_ref,
                                    &dynamic_constants_ref,
                                    &types_ref,
                                    def_use,
                                )
                            })
                            .collect();
                    // Inlining is special in that it may modify partitions in a
                    // inter-procedural fashion.
                    inline(&mut editors, callgraph, self.plans.as_mut());
                    self.module.constants = constants_ref.take();
                    self.module.dynamic_constants = dynamic_constants_ref.take();
                    self.module.types = types_ref.take();
                    let edits: Vec<_> = editors.into_iter().map(|editor| editor.edits()).collect();
                    for idx in 0..edits.len() {
                        if let Some(plans) = self.plans.as_mut() {
                            repair_plan(&mut plans[idx], &self.module.functions[idx], &edits[idx]);
                        }
                        let grave_mapping = self.module.functions[idx].delete_gravestones();
                        if let Some(plans) = self.plans.as_mut() {
                            plans[idx].fix_gravestones(&grave_mapping);
                        }
                    }
                    self.clear_analyses();
                }
                Pass::Verify => {
                    let (
                        def_uses,
                        reverse_postorders,
                        typing,
                        subgraphs,
                        doms,
                        postdoms,
                        fork_join_maps,
                    ) = verify(&mut self.module)
                        .expect("PANIC: Failed to verify Hercules IR module.");

                    // Verification produces a bunch of analysis results that
                    // may be useful for later passes.
                    self.def_uses = Some(def_uses);
                    self.reverse_postorders = Some(reverse_postorders);
                    self.typing = Some(typing);
                    self.control_subgraphs = Some(subgraphs);
                    self.doms = Some(doms);
                    self.postdoms = Some(postdoms);
                    self.fork_join_maps = Some(fork_join_maps);

                    // Verify the plan, if it exists.
                    if let Some(plans) = &self.plans {
                        for idx in 0..self.module.functions.len() {
                            plans[idx].verify_partitioning(
                                &self.module.functions[idx],
                                &self.def_uses.as_ref().unwrap()[idx],
                                &self.fork_join_maps.as_ref().unwrap()[idx],
                            );
                        }
                    }
                }
                Pass::Xdot(force_analyses) => {
                    self.make_reverse_postorders();
                    if *force_analyses {
                        self.make_doms();
                        self.make_fork_join_maps();
                        self.make_bbs();
                        self.make_plans();
                    }
                    xdot_module(
                        &self.module,
                        self.reverse_postorders.as_ref().unwrap(),
                        self.doms.as_ref(),
                        self.fork_join_maps.as_ref(),
                        self.bbs.as_ref(),
                        self.plans.as_ref(),
                    );
                }
                Pass::SchedXdot => {
                    self.make_def_uses();
                    self.make_typing();
                    self.make_control_subgraphs();
                    self.make_fork_join_maps();
                    self.make_fork_join_nests();
                    self.make_antideps();
                    self.make_bbs();
                    self.make_plans();

                    let smodule = sched_compile(
                        &self.module,
                        self.def_uses.as_ref().unwrap(),
                        self.typing.as_ref().unwrap(),
                        self.control_subgraphs.as_ref().unwrap(),
                        self.fork_join_maps.as_ref().unwrap(),
                        self.fork_join_nests.as_ref().unwrap(),
                        self.antideps.as_ref().unwrap(),
                        self.bbs.as_ref().unwrap(),
                        self.plans.as_ref().unwrap(),
                    );

                    xdot_sched_module(&smodule);
                }
                Pass::Codegen(output_dir, module_name) => {
                    self.make_def_uses();
                    self.make_typing();
                    self.make_control_subgraphs();
                    self.make_fork_join_maps();
                    self.make_fork_join_nests();
                    self.make_antideps();
                    self.make_bbs();
                    self.make_plans();

                    let smodule = sched_compile(
                        &self.module,
                        self.def_uses.as_ref().unwrap(),
                        self.typing.as_ref().unwrap(),
                        self.control_subgraphs.as_ref().unwrap(),
                        self.fork_join_maps.as_ref().unwrap(),
                        self.fork_join_nests.as_ref().unwrap(),
                        self.antideps.as_ref().unwrap(),
                        self.bbs.as_ref().unwrap(),
                        self.plans.as_ref().unwrap(),
                    );

                    let mut llvm_ir = String::new();
                    for manifest in smodule.manifests.values() {
                        for partition_manifest in manifest.partitions.iter() {
                            let function = &smodule.functions[&partition_manifest.name];
                            match partition_manifest.device {
                                DeviceManifest::CPU { parallel_launch: _ } => {
                                    cpu_compile(function, partition_manifest, &mut llvm_ir).unwrap()
                                }
                                _ => todo!(),
                            }
                        }
                    }
                    println!("{}", llvm_ir);

                    // Write the LLVM IR into a temporary file.
                    let mut tmp_path = temp_dir();
                    tmp_path.push(format!("{}.ll", module_name));
                    let mut file = File::create(&tmp_path)
                        .expect("PANIC: Unable to open output LLVM IR file.");
                    file.write_all(llvm_ir.as_bytes())
                        .expect("PANIC: Unable to write output LLVM IR file contents.");
                    println!("{}", tmp_path.display());

                    // Compile LLVM IR into an ELF object file.
                    let output_archive = format!("{}/lib{}.a", output_dir, module_name);
                    let mut clang_process = Command::new("clang")
                        .arg(&tmp_path)
                        .arg("--emit-static-lib")
                        .arg("-O3")
                        .arg("-march=native")
                        .arg("-o")
                        .arg(&output_archive)
                        .stdin(Stdio::piped())
                        .stdout(Stdio::piped())
                        .spawn()
                        .expect("Error running clang. Is it installed?");
                    assert!(clang_process.wait().unwrap().success());
                    println!("{}", output_archive);

                    // Package manifest into a file.
                    let hman_contents: Vec<u8> = postcard::to_allocvec(&smodule.manifests).unwrap();
                    let mut file = File::create(format!("{}/{}.hman", output_dir, module_name))
                        .expect("PANIC: Unable to open output manifest file.");
                    file.write_all(&hman_contents)
                        .expect("PANIC: Unable to write output manifest file contents.");
                    self.manifests = Some(smodule.manifests);
                    println!("{:?}", self.manifests);
                }
                Pass::Serialize(output_file) => {
                    let module_contents: Vec<u8> = postcard::to_allocvec(&self.module).unwrap();
                    let mut file = File::create(&output_file)
                        .expect("PANIC: Unable to open output module file.");
                    file.write_all(&module_contents)
                        .expect("PANIC: Unable to write output module file contents.");
                }
            }
        }
    }

    fn legacy_repair_plan(&mut self) {
        // Cleanup the module after passes. Delete gravestone nodes. Repair the
        // plans.
        for idx in 0..self.module.functions.len() {
            let grave_mapping = self.module.functions[idx].delete_gravestones();
            let plans = &mut self.plans;
            let functions = &self.module.functions;
            if let Some(plans) = plans.as_mut() {
                take_mut::take(&mut plans[idx], |plan| {
                    plan.repair(&functions[idx], &grave_mapping)
                });
            }
        }
    }

    fn clear_analyses(&mut self) {
        self.def_uses = None;
        self.reverse_postorders = None;
        self.typing = None;
        self.control_subgraphs = None;
        self.doms = None;
        self.postdoms = None;
        self.fork_join_maps = None;
        self.fork_join_nests = None;
        self.loops = None;
        self.antideps = None;
        self.bbs = None;
        self.callgraph = None;

        // Don't clear the plan - this is repaired, not reconstructed.
    }

    pub fn get_module(self) -> Module {
        self.module
    }

    pub fn get_manifests(self) -> HashMap<String, Manifest> {
        self.manifests.unwrap()
    }
}