spirt::passes::link

Function minimize_exports

source
pub fn minimize_exports(
    module: &mut Module,
    is_root: impl Fn(&ExportKey) -> bool,
)
Expand description

Remove exports which aren’t “roots” (is_root(export_key) returns false), and which aren’t otherwise kept alive by a “root” (through Import::LinkName declarations, with name matching ExportKey::LinkName), either directly or transitively (including through any number of imports).

In essence, other than the “root” exports, minimize_exports only keeps the exports that resolve_imports would use, and is recommended to first call minimize_exports before using resolve_imports, to reduce work.

Note that the “dead” definitions are not removed from the module, and any external references to them could still be used (e.g. from a clone of the module.exports map, before calling minimize_exports).

Examples found in repository?
examples/spv-lower-link-lift.rs (lines 58-60)
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
fn main() -> std::io::Result<()> {
    match &std::env::args().collect::<Vec<_>>()[..] {
        [_, in_file] => {
            let in_file_path = Path::new(in_file);

            let save_print_plan = |suffix: &str, plan: spirt::print::Plan| {
                let pretty = plan.pretty_print();
                let ext = format!("{suffix}.spirt");

                // FIXME(eddyb) don't allocate whole `String`s here.
                fs::write(in_file_path.with_extension(&ext), pretty.to_string())?;
                fs::write(
                    in_file_path.with_extension(ext + ".html"),
                    pretty.render_to_html().with_dark_mode_support().to_html_doc(),
                )
            };

            // FIXME(eddyb) adapt the other examples to this style.

            fn eprint_duration<R>(f: impl FnOnce() -> R) -> R {
                let start = std::time::Instant::now();
                let r = f();
                eprint!("[{:8.3}ms] ", start.elapsed().as_secs_f64() * 1000.0);
                r
            }

            eprint_duration(|| {
                let _ = spirt::spv::spec::Spec::get();
            });
            eprintln!("spv::spec::Spec::get");

            let cx = Rc::new(spirt::Context::new());

            let multi_version_printing = true;
            let mut per_pass_module = vec![];
            let mut after_pass = |pass, module: &spirt::Module| {
                if multi_version_printing {
                    per_pass_module.push((pass, module.clone()));
                    Ok(())
                } else {
                    save_print_plan(
                        &format!("after.{pass}"),
                        spirt::print::Plan::for_module(module),
                    )
                }
            };

            let mut module =
                eprint_duration(|| spirt::Module::lower_from_spv_file(cx.clone(), in_file_path))?;
            eprintln!("Module::lower_from_spv_file({})", in_file_path.display());

            let original_export_count = module.exports.len();
            eprint_duration(|| {
                spirt::passes::link::minimize_exports(&mut module, |export_key| {
                    matches!(export_key, spirt::ExportKey::SpvEntryPoint { .. })
                })
            });
            eprintln!(
                "link::minimize_exports: {} -> {} exports",
                original_export_count,
                module.exports.len()
            );
            after_pass("minimize_exports", &module)?;

            // HACK(eddyb) do this late enough to avoid spending time on unused
            // functions, which `link::minimize_exports` makes unreachable.
            eprint_duration(|| spirt::passes::legalize::structurize_func_cfgs(&mut module));
            eprintln!("legalize::structurize_func_cfgs");
            after_pass("structurize_func_cfgs", &module)?;

            eprint_duration(|| spirt::passes::link::resolve_imports(&mut module));
            eprintln!("link::resolve_imports");
            after_pass("resolve_imports", &module)?;

            if multi_version_printing {
                // FIXME(eddyb) use a better suffix than `link` (or none).
                save_print_plan(
                    "link",
                    spirt::print::Plan::for_versions(
                        &cx,
                        per_pass_module
                            .iter()
                            .map(|(pass, module)| (format!("after {pass}"), module)),
                    ),
                )?;
            }

            let out_file_path = in_file_path.with_extension("link.spv");
            eprint_duration(|| module.lift_to_spv_file(&out_file_path))?;
            eprintln!("Module::lift_to_spv_file({})", out_file_path.display());

            Ok(())
        }
        args => {
            eprintln!("Usage: {} IN", args[0]);
            std::process::exit(1);
        }
    }
}
More examples
Hide additional examples
examples/spv-lower-link-qptr-lift.rs (lines 58-60)
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
fn main() -> std::io::Result<()> {
    match &std::env::args().collect::<Vec<_>>()[..] {
        [_, in_file] => {
            let in_file_path = Path::new(in_file);

            let save_print_plan = |suffix: &str, plan: spirt::print::Plan| {
                let pretty = plan.pretty_print();
                let ext = format!("{suffix}.spirt");

                // FIXME(eddyb) don't allocate whole `String`s here.
                fs::write(in_file_path.with_extension(&ext), pretty.to_string())?;
                fs::write(
                    in_file_path.with_extension(ext + ".html"),
                    pretty.render_to_html().with_dark_mode_support().to_html_doc(),
                )
            };

            // FIXME(eddyb) adapt the other examples to this style.

            fn eprint_duration<R>(f: impl FnOnce() -> R) -> R {
                let start = std::time::Instant::now();
                let r = f();
                eprint!("[{:8.3}ms] ", start.elapsed().as_secs_f64() * 1000.0);
                r
            }

            eprint_duration(|| {
                let _ = spirt::spv::spec::Spec::get();
            });
            eprintln!("spv::spec::Spec::get");

            let cx = Rc::new(spirt::Context::new());

            let multi_version_printing = true;
            let mut per_pass_module = vec![];
            let mut after_pass = |pass, module: &spirt::Module| {
                if multi_version_printing {
                    per_pass_module.push((pass, module.clone()));
                    Ok(())
                } else {
                    save_print_plan(
                        &format!("after.{pass}"),
                        spirt::print::Plan::for_module(module),
                    )
                }
            };

            let mut module =
                eprint_duration(|| spirt::Module::lower_from_spv_file(cx.clone(), in_file_path))?;
            eprintln!("Module::lower_from_spv_file({})", in_file_path.display());

            let original_export_count = module.exports.len();
            eprint_duration(|| {
                spirt::passes::link::minimize_exports(&mut module, |export_key| {
                    matches!(export_key, spirt::ExportKey::SpvEntryPoint { .. })
                })
            });
            eprintln!(
                "link::minimize_exports: {} -> {} exports",
                original_export_count,
                module.exports.len()
            );
            //after_pass("minimize_exports", &module)?;

            // HACK(eddyb) do this late enough to avoid spending time on unused
            // functions, which `link::minimize_exports` makes unreachable.
            eprint_duration(|| spirt::passes::legalize::structurize_func_cfgs(&mut module));
            eprintln!("legalize::structurize_func_cfgs");
            //after_pass("structurize_func_cfgs", &module)?;

            eprint_duration(|| spirt::passes::link::resolve_imports(&mut module));
            eprintln!("link::resolve_imports");
            //after_pass("resolve_imports", &module)?;

            // HACK(eddyb)
            after_pass("", &module)?;

            // HACK(eddyb) this is roughly what Rust-GPU would need.
            let layout_config = &spirt::qptr::LayoutConfig {
                abstract_bool_size_align: (1, 1),
                logical_ptr_size_align: (4, 4),
                ..spirt::qptr::LayoutConfig::VULKAN_SCALAR_LAYOUT
            };

            eprint_duration(|| {
                spirt::passes::qptr::lower_from_spv_ptrs(&mut module, layout_config)
            });
            eprintln!("qptr::lower_from_spv_ptrs");
            after_pass("qptr::lower_from_spv_ptrs", &module)?;

            eprint_duration(|| spirt::passes::qptr::analyze_uses(&mut module, layout_config));
            eprintln!("qptr::analyze_uses");
            after_pass("qptr::analyze_uses", &module)?;

            eprint_duration(|| spirt::passes::qptr::lift_to_spv_ptrs(&mut module, layout_config));
            eprintln!("qptr::lift_to_spv_ptrs");
            after_pass("qptr::lift_to_spv_ptrs", &module)?;

            if multi_version_printing {
                // FIXME(eddyb) use a better suffix than `qptr` (or none).
                save_print_plan(
                    "qptr",
                    spirt::print::Plan::for_versions(
                        &cx,
                        per_pass_module.iter().map(|(pass, module)| {
                            (
                                // HACK(eddyb)
                                if pass.is_empty() {
                                    "initial".into()
                                } else {
                                    format!("after {pass}")
                                },
                                module,
                            )
                        }),
                    ),
                )?;
            }

            //let out_file_path = in_file_path.with_extension("qptr.spv");
            //eprint_duration(|| module.lift_to_spv_file(&out_file_path))?;
            //eprintln!("Module::lift_to_spv_file({})", out_file_path.display());

            Ok(())
        }
        args => {
            eprintln!("Usage: {} IN", args[0]);
            std::process::exit(1);
        }
    }
}