From ccd25187bac743539410c48fe0d25506089e0440 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 30 Oct 2025 03:37:05 +0200 Subject: [PATCH] Add `--dump-spirt` for dumping only the final SPIR-T module (unlike `--dump-spirt-passes`). --- .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 29 ++++++++++++-- crates/rustc_codegen_spirv/src/linker/mod.rs | 40 ++++++++++++++----- docs/src/codegen-args.md | 8 ++++ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index 1d160e2a8ff..4715a7461ba 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -368,16 +368,19 @@ pub struct CodegenArgs { impl CodegenArgs { pub fn from_session(sess: &Session) -> Self { - match CodegenArgs::parse(&sess.opts.cg.llvm_args) { + match Self::parse(sess, &sess.opts.cg.llvm_args) { Ok(ok) => ok, + + // FIXME(eddyb) this should mention `RUSTGPU_CODEGEN_ARGS`, just + // like how `RUSTGPU_CODEGEN_ARGS=--help` is already special-cased. Err(err) => sess .dcx() .fatal(format!("Unable to parse llvm-args: {err}")), } } - // FIXME(eddyb) `structopt` would come a long way to making this nicer. - pub fn parse(args: &[String]) -> Result { + // FIXME(eddyb) switch all of this over to `clap`. + pub fn parse(sess: &Session, args: &[String]) -> Result { use rustc_session::getopts; // FIXME(eddyb) figure out what casing ("Foo bar" vs "foo bar") to use @@ -496,6 +499,12 @@ impl CodegenArgs { "dump the SPIR-T module across passes, to a (pair of) file(s) in DIR", "DIR", ); + opts.optopt( + "", + "dump-spirt", + "dump the final SPIR-T module, to a (pair of) file(s) in DIR", + "DIR", + ); opts.optflag( "", "spirt-strip-custom-debuginfo-from-dumps", @@ -663,7 +672,19 @@ impl CodegenArgs { dump_pre_inline: matches_opt_dump_dir_path("dump-pre-inline"), dump_post_inline: matches_opt_dump_dir_path("dump-post-inline"), dump_post_split: matches_opt_dump_dir_path("dump-post-split"), - dump_spirt_passes: matches_opt_dump_dir_path("dump-spirt-passes"), + dump_spirt: match ["dump-spirt-passes", "dump-spirt"].map(matches_opt_dump_dir_path) { + [Some(dump_spirt_passes), dump_spirt] => { + if dump_spirt.is_some() { + sess.dcx() + .warn("`--dump-spirt` ignored in favor of `--dump-spirt-passes`"); + } + Some((dump_spirt_passes, crate::linker::DumpSpirtMode::AllPasses)) + } + [None, Some(dump_spirt)] => { + Some((dump_spirt, crate::linker::DumpSpirtMode::OnlyFinal)) + } + [None, None] => None, + }, spirt_strip_custom_debuginfo_from_dumps: matches .opt_present("spirt-strip-custom-debuginfo-from-dumps"), spirt_keep_debug_sources_in_dumps: matches diff --git a/crates/rustc_codegen_spirv/src/linker/mod.rs b/crates/rustc_codegen_spirv/src/linker/mod.rs index 3001e95fefd..98e076bf5fe 100644 --- a/crates/rustc_codegen_spirv/src/linker/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/mod.rs @@ -62,13 +62,19 @@ pub struct Options { pub dump_pre_inline: Option, pub dump_post_inline: Option, pub dump_post_split: Option, - pub dump_spirt_passes: Option, + pub dump_spirt: Option<(PathBuf, DumpSpirtMode)>, pub spirt_strip_custom_debuginfo_from_dumps: bool, pub spirt_keep_debug_sources_in_dumps: bool, pub spirt_keep_unstructured_cfg_in_dumps: bool, pub specializer_dump_instances: Option, } +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DumpSpirtMode { + AllPasses, + OnlyFinal, +} + pub enum LinkResult { SingleModule(Box), MultipleModules { @@ -531,7 +537,7 @@ pub fn link( drop(timer); let pass_name = dump_guard.in_progress_pass_name.take().unwrap(); if let Some(module) = module - && opts.dump_spirt_passes.is_some() + && matches!(opts.dump_spirt, Some((_, DumpSpirtMode::AllPasses))) { dump_guard .per_pass_module_for_dumping @@ -812,9 +818,9 @@ impl Drop for SpirtDumpGuard<'_> { let mut dump_spirt_file_path = self.linker_options - .dump_spirt_passes + .dump_spirt .as_ref() - .map(|dump_dir| { + .map(|(dump_dir, _)| { dump_dir .join(self.disambiguated_crate_name_for_dumps) .with_extension("spirt") @@ -826,10 +832,6 @@ impl Drop for SpirtDumpGuard<'_> { // but that requires keeping around e.g. the initial SPIR-V for longer, // and probably invoking the "SPIR-T pipeline" here, as looping is hard). if self.any_spirt_bugs && dump_spirt_file_path.is_none() { - if self.per_pass_module_for_dumping.is_empty() { - self.per_pass_module_for_dumping - .push(("".into(), self.module.clone())); - } dump_spirt_file_path = Some(self.outputs.temp_path_for_diagnostic("spirt")); } @@ -837,6 +839,11 @@ impl Drop for SpirtDumpGuard<'_> { return; }; + if self.per_pass_module_for_dumping.is_empty() { + self.per_pass_module_for_dumping + .push(("".into(), self.module.clone())); + } + for (_, module) in &mut self.per_pass_module_for_dumping { // FIXME(eddyb) consider catching panics in this? self.linker_options.spirt_cleanup_for_dumping(module); @@ -846,7 +853,16 @@ impl Drop for SpirtDumpGuard<'_> { let versions = self .per_pass_module_for_dumping .iter() - .map(|(pass_name, module)| (format!("after {pass_name}"), module)); + .map(|(pass_name, module)| { + ( + if pass_name.is_empty() { + "".into() + } else { + format!("after {pass_name}") + }, + module, + ) + }); let mut panicked_printing_after_passes = None; for truncate_version_count in (1..=versions.len()).rev() { @@ -907,7 +923,11 @@ impl Drop for SpirtDumpGuard<'_> { "pretty-printed SPIR-T was saved to {}.html", dump_spirt_file_path.display() )); - if self.linker_options.dump_spirt_passes.is_none() { + let is_dumping_spirt_passes = matches!( + self.linker_options.dump_spirt, + Some((_, DumpSpirtMode::AllPasses)) + ); + if !is_dumping_spirt_passes { note.help("re-run with `RUSTGPU_CODEGEN_ARGS=\"--dump-spirt-passes=$PWD\"` for more details"); } note.note("pretty-printed SPIR-T is preferred when reporting Rust-GPU issues"); diff --git a/docs/src/codegen-args.md b/docs/src/codegen-args.md index 71fb69d6d2d..687baf0cf09 100644 --- a/docs/src/codegen-args.md +++ b/docs/src/codegen-args.md @@ -175,6 +175,14 @@ _Note: passes that are not already enabled by default are considered experimenta Dump the `SPIR-🇹` module across passes (i.e. all of the versions before/after each pass), as a combined report, to a pair of files (`.spirt` and `.spirt.html`) in `DIR`. (the `.spirt.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) +Mutually exclusive with `--dump-spirt` (this takes precedence over that). + +### `--dump-spirt DIR` + +Dump the `SPIR-🇹` module, similar to `--dump-spirt-passes`, but only the final version. + +Mutually exclusive with `--dump-spirt-passes` (which takes precedence over this). + ### `--spirt-strip-custom-debuginfo-from-dumps` When dumping (pretty-printed) `SPIR-🇹` (e.g. with `--dump-spirt-passes`), strip