mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-25 00:04:11 +00:00
Minimally integrate SPIR-T (opt-in via RUSTGPU_CODEGEN_ARGS=--spirt
).
This commit is contained in:
parent
78130e1151
commit
8535bb3bf1
@ -34,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `RUSTGPU_RUSTFLAGS="..."` for shader `RUSTFLAGS="..."`
|
||||
- `RUSTGPU_CODEGEN_ARGS="..."` for shader "codegen args" (i.e. `RUSTFLAGS=-Cllvm-args="..."`)
|
||||
(check out ["codegen args" docs](docs/src/codegen-args.md), or run with `RUSTGPU_CODEGEN_ARGS=--help` to see the full list of options)
|
||||
- [PR#940](https://github.com/EmbarkStudios/rust-gpu/pull/940) integrated the experimental [`SPIR-🇹` shader IR framework](https://github.com/EmbarkStudios/spirt) into the linker
|
||||
(opt-in via `RUSTGPU_CODEGEN_ARGS=--spirt`, see also [the `--spirt` docs](docs/src/codegen-args.md#--spirt), for more details)
|
||||
|
||||
### Changed 🛠️
|
||||
- [PR#958](https://github.com/EmbarkStudios/rust-gpu/pull/958) updated toolchain to `nightly-2022-10-29`
|
||||
|
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -683,6 +683,16 @@ version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "elsa"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b4b5d23ed6b6948d68240aafa4ac98e568c9a020efd9d4201a6288bc3006e09"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.30"
|
||||
@ -1096,6 +1106,15 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
@ -2007,6 +2026,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"spirt",
|
||||
"spirv-tools",
|
||||
"syn",
|
||||
"tempfile",
|
||||
@ -2190,6 +2210,9 @@ name = "smallvec"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
@ -2210,6 +2233,23 @@ dependencies = [
|
||||
"wayland-protocols 0.29.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/EmbarkStudios/spirt.git?rev=cda161b7e7b336685448ab0a7f8cfe96bd90e752#cda161b7e7b336685448ab0a7f8cfe96bd90e752"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"elsa",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv"
|
||||
version = "0.2.0+1.5.4"
|
||||
@ -2278,6 +2318,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -52,3 +52,7 @@ codegen-units = 256
|
||||
opt-level = 3
|
||||
incremental = true
|
||||
codegen-units = 256
|
||||
|
||||
[patch.crates-io]
|
||||
# HACK(eddyb) only needed until `spirt 0.1.0` is released.
|
||||
spirt = { git = "https://github.com/EmbarkStudios/spirt.git", rev = "cda161b7e7b336685448ab0a7f8cfe96bd90e752" }
|
||||
|
@ -56,6 +56,7 @@ serde_json = "1.0"
|
||||
smallvec = { version = "1.6.1", features = ["union"] }
|
||||
spirv-tools = { version = "0.9", default-features = false }
|
||||
rustc_codegen_spirv-types.workspace = true
|
||||
spirt = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pipe = "0.4"
|
||||
|
@ -351,6 +351,12 @@ impl CodegenArgs {
|
||||
);
|
||||
opts.optflag("", "no-structurize", "disables CFG structurization");
|
||||
|
||||
opts.optflag(
|
||||
"",
|
||||
"spirt",
|
||||
"use SPIR-T for legalization (see also `docs/src/codegen-args.md`)",
|
||||
);
|
||||
|
||||
// NOTE(eddyb) these are debugging options that used to be env vars
|
||||
// (for more information see `docs/src/codegen-args.md`).
|
||||
opts.optopt(
|
||||
@ -365,6 +371,12 @@ impl CodegenArgs {
|
||||
"dump modules immediately after multimodule splitting, to files in DIR",
|
||||
"DIR",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"dump-spirt-passes",
|
||||
"dump the SPIR-T module across passes, to FILE and FILE.html",
|
||||
"FILE",
|
||||
);
|
||||
opts.optflag(
|
||||
"",
|
||||
"specializer-debug",
|
||||
@ -511,6 +523,7 @@ impl CodegenArgs {
|
||||
dce: !matches.opt_present("no-dce"),
|
||||
compact_ids: !matches.opt_present("no-compact-ids"),
|
||||
structurize: !matches.opt_present("no-structurize"),
|
||||
spirt: matches.opt_present("spirt"),
|
||||
|
||||
// FIXME(eddyb) deduplicate between `CodegenArgs` and `linker::Options`.
|
||||
emit_multiple_modules: module_output_type == ModuleOutputType::Multiple,
|
||||
@ -521,6 +534,7 @@ impl CodegenArgs {
|
||||
// (for more information see `docs/src/codegen-args.md`).
|
||||
dump_post_merge: matches_opt_path("dump-post-merge"),
|
||||
dump_post_split: matches_opt_dump_dir_path("dump-post-split"),
|
||||
dump_spirt_passes: matches_opt_path("dump-spirt-passes"),
|
||||
specializer_debug: matches.opt_present("specializer-debug"),
|
||||
specializer_dump_instances: matches_opt_path("specializer-dump-instances"),
|
||||
print_all_zombie: matches.opt_present("print-all-zombie"),
|
||||
|
@ -34,6 +34,7 @@ pub struct Options {
|
||||
pub compact_ids: bool,
|
||||
pub dce: bool,
|
||||
pub structurize: bool,
|
||||
pub spirt: bool,
|
||||
|
||||
pub emit_multiple_modules: bool,
|
||||
pub spirv_metadata: SpirvMetadata,
|
||||
@ -48,6 +49,7 @@ pub struct Options {
|
||||
// (for more information see `docs/src/codegen-args.md`).
|
||||
pub dump_post_merge: Option<PathBuf>,
|
||||
pub dump_post_split: Option<PathBuf>,
|
||||
pub dump_spirt_passes: Option<PathBuf>,
|
||||
pub specializer_debug: bool,
|
||||
pub specializer_dump_instances: Option<PathBuf>,
|
||||
pub print_all_zombie: bool,
|
||||
@ -141,10 +143,10 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
|
||||
bound += module.header.as_ref().unwrap().bound - 1;
|
||||
let this_version = module.header.as_ref().unwrap().version();
|
||||
if version != this_version {
|
||||
sess.fatal(format!(
|
||||
return Err(sess.err(format!(
|
||||
"cannot link two modules with different SPIR-V versions: v{}.{} and v{}.{}",
|
||||
version.0, version.1, this_version.0, this_version.1
|
||||
))
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,25 +236,14 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let _timer = sess.timer("link_inline");
|
||||
inline::inline(sess, &mut output)?;
|
||||
}
|
||||
// NOTE(eddyb) with SPIR-T, we can do `mem2reg` before inlining, too!
|
||||
if opts.spirt {
|
||||
if opts.dce {
|
||||
let _timer = sess.timer("link_dce-before-inlining");
|
||||
dce::dce(&mut output);
|
||||
}
|
||||
|
||||
if opts.dce {
|
||||
let _timer = sess.timer("link_dce");
|
||||
dce::dce(&mut output);
|
||||
}
|
||||
|
||||
let mut output = if opts.structurize {
|
||||
let _timer = sess.timer("link_structurize");
|
||||
structurizer::structurize(output)
|
||||
} else {
|
||||
output
|
||||
};
|
||||
|
||||
{
|
||||
let _timer = sess.timer("link_block_ordering_pass_and_mem2reg");
|
||||
let _timer = sess.timer("link_block_ordering_pass_and_mem2reg-before-inlining");
|
||||
let mut pointer_to_pointee = FxHashMap::default();
|
||||
let mut constants = FxHashMap::default();
|
||||
let mut u32 = None;
|
||||
@ -290,6 +281,142 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let _timer = sess.timer("link_inline");
|
||||
inline::inline(sess, &mut output)?;
|
||||
}
|
||||
|
||||
if opts.dce {
|
||||
let _timer = sess.timer("link_dce-after-inlining");
|
||||
dce::dce(&mut output);
|
||||
}
|
||||
|
||||
let mut output = if opts.structurize && !opts.spirt {
|
||||
let _timer = sess.timer("link_structurize");
|
||||
structurizer::structurize(output)
|
||||
} else {
|
||||
output
|
||||
};
|
||||
|
||||
{
|
||||
let _timer = sess.timer("link_block_ordering_pass_and_mem2reg-after-inlining");
|
||||
let mut pointer_to_pointee = FxHashMap::default();
|
||||
let mut constants = FxHashMap::default();
|
||||
let mut u32 = None;
|
||||
for inst in &output.types_global_values {
|
||||
match inst.class.opcode {
|
||||
Op::TypePointer => {
|
||||
pointer_to_pointee
|
||||
.insert(inst.result_id.unwrap(), inst.operands[1].unwrap_id_ref());
|
||||
}
|
||||
Op::TypeInt
|
||||
if inst.operands[0].unwrap_literal_int32() == 32
|
||||
&& inst.operands[1].unwrap_literal_int32() == 0 =>
|
||||
{
|
||||
assert!(u32.is_none());
|
||||
u32 = Some(inst.result_id.unwrap());
|
||||
}
|
||||
Op::Constant if u32.is_some() && inst.result_type == u32 => {
|
||||
let value = inst.operands[0].unwrap_literal_int32();
|
||||
constants.insert(inst.result_id.unwrap(), value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for func in &mut output.functions {
|
||||
simple_passes::block_ordering_pass(func);
|
||||
// Note: mem2reg requires functions to be in RPO order (i.e. block_ordering_pass)
|
||||
mem2reg::mem2reg(
|
||||
output.header.as_mut().unwrap(),
|
||||
&mut output.types_global_values,
|
||||
&pointer_to_pointee,
|
||||
&constants,
|
||||
func,
|
||||
);
|
||||
destructure_composites::destructure_composites(func);
|
||||
}
|
||||
}
|
||||
|
||||
if opts.spirt {
|
||||
let mut per_pass_module_for_dumping = vec![];
|
||||
let mut after_pass = |pass, module: &spirt::Module| {
|
||||
if opts.dump_spirt_passes.is_some() {
|
||||
per_pass_module_for_dumping.push((pass, module.clone()));
|
||||
}
|
||||
};
|
||||
|
||||
let spv_bytes = {
|
||||
let _timer = sess.timer("assemble-to-spv_bytes-for-spirt");
|
||||
spirv_tools::binary::from_binary(&output.assemble()).to_vec()
|
||||
};
|
||||
let cx = std::rc::Rc::new(spirt::Context::new());
|
||||
let mut module = {
|
||||
let _timer = sess.timer("spirt::Module::lower_from_spv_file");
|
||||
match spirt::Module::lower_from_spv_bytes(cx.clone(), spv_bytes) {
|
||||
Ok(module) => module,
|
||||
Err(e) => {
|
||||
use rspirv::binary::Disassemble;
|
||||
|
||||
return Err(sess
|
||||
.struct_err(format!("{e}"))
|
||||
.note(format!(
|
||||
"while lowering this SPIR-V module to SPIR-T:\n{}",
|
||||
output.disassemble()
|
||||
))
|
||||
.emit());
|
||||
}
|
||||
}
|
||||
};
|
||||
after_pass("lower_from_spv", &module);
|
||||
|
||||
if opts.structurize {
|
||||
{
|
||||
let _timer = sess.timer("spirt::legalize::structurize_func_cfgs");
|
||||
spirt::passes::legalize::structurize_func_cfgs(&mut module);
|
||||
}
|
||||
after_pass("structurize_func_cfgs", &module);
|
||||
}
|
||||
|
||||
// NOTE(eddyb) this should be *before* `lift_to_spv` below,
|
||||
// so if that fails, the dump could be used to debug it.
|
||||
if let Some(dump_file) = &opts.dump_spirt_passes {
|
||||
// HACK(eddyb) using `.with_extension(...)` may replace part of a
|
||||
// `.`-containing file name, but we want to always append `.html`.
|
||||
let mut dump_file_html = dump_file.as_os_str().to_owned();
|
||||
dump_file_html.push(".html");
|
||||
|
||||
let plan = spirt::print::Plan::for_versions(
|
||||
&cx,
|
||||
per_pass_module_for_dumping
|
||||
.iter()
|
||||
.map(|(pass, module)| (format!("after {pass}"), module)),
|
||||
);
|
||||
let pretty = plan.pretty_print();
|
||||
|
||||
// FIXME(eddyb) don't allocate whole `String`s here.
|
||||
std::fs::write(dump_file, pretty.to_string()).unwrap();
|
||||
std::fs::write(
|
||||
dump_file_html,
|
||||
pretty
|
||||
.render_to_html()
|
||||
.with_dark_mode_support()
|
||||
.to_html_doc(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let spv_words = {
|
||||
let _timer = sess.timer("spirt::Module::lift_to_spv_module_emitter");
|
||||
module.lift_to_spv_module_emitter().unwrap().words
|
||||
};
|
||||
output = {
|
||||
let _timer = sess.timer("parse-spv_words-from-spirt");
|
||||
let mut loader = Loader::new();
|
||||
rspirv::binary::parse_words(&spv_words, &mut loader).unwrap();
|
||||
loader.module()
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
let _timer = sess.timer("peephole_opts");
|
||||
let types = peephole_opts::collect_types(&output);
|
||||
|
@ -138,3 +138,18 @@ anyway, be careful).
|
||||
### `--no-structurize`
|
||||
|
||||
Disables CFG structurization. Probably results in invalid modules.
|
||||
|
||||
### `--spirt`
|
||||
|
||||
Enables using the experimental [`SPIR-🇹` shader IR framework](https://github.com/EmbarkStudios/spirt) in the linker - more specifically, this:
|
||||
- adds a `SPIR-V -> SPIR-🇹 -> SPIR-V` roundtrip
|
||||
(future `SPIR-🇹` passes would go in the middle of this, and eventually codegen might not produce `SPIR-V` at all)
|
||||
- replaces the existing structurizer with `SPIR-🇹` structurization (which is more robust and can e.g. handle `OpPhi`s)
|
||||
- runs some existing `SPIR-V` legalization/optimization passes (`mem2reg`) *before* inlining, instead of *only after* (as the `OpPhi`s they would produce are no longer an issue for structurization)
|
||||
|
||||
For more information, also see [the `SPIR-🇹` repository](https://github.com/EmbarkStudios/spirt).
|
||||
|
||||
### `--dump-spirt-passes FILE`
|
||||
|
||||
Dump the `SPIR-🇹` module across passes (i.e. all of the versions before/after each pass), as a combined report, to `FILE` and `FILE.html`.
|
||||
<sub>(the `.html` version of the report is the recommended form for viewing, as it uses tabling for versions, syntax-highlighting-like styling, and use->def linking)</sub>
|
||||
|
Loading…
Reference in New Issue
Block a user