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
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);
}
}
}