Skip to content

Commit 7f508ba

Browse files
committed
linker: use OutputFilenames::temp_path_ext for critical dumping, even without --dump-*.
1 parent 1abd1cf commit 7f508ba

File tree

4 files changed

+92
-37
lines changed

4 files changed

+92
-37
lines changed

crates/rustc_codegen_spirv/src/link.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub fn link(
7878
crate_type,
7979
&out_filename,
8080
codegen_results,
81+
outputs,
8182
&disambiguated_crate_name_for_dumps,
8283
);
8384
}
@@ -123,6 +124,7 @@ fn link_exe(
123124
crate_type: CrateType,
124125
out_filename: &Path,
125126
codegen_results: &CodegenResults,
127+
outputs: &OutputFilenames,
126128
disambiguated_crate_name_for_dumps: &OsStr,
127129
) {
128130
let mut objects = Vec::new();
@@ -152,6 +154,7 @@ fn link_exe(
152154
&cg_args,
153155
&objects,
154156
&rlibs,
157+
outputs,
155158
disambiguated_crate_name_for_dumps,
156159
);
157160
let compile_result = match link_result {
@@ -517,6 +520,7 @@ fn do_link(
517520
cg_args: &CodegenArgs,
518521
objects: &[PathBuf],
519522
rlibs: &[PathBuf],
523+
outputs: &OutputFilenames,
520524
disambiguated_crate_name_for_dumps: &OsStr,
521525
) -> linker::LinkResult {
522526
let load_modules_timer = sess.timer("link_load_modules");
@@ -570,6 +574,7 @@ fn do_link(
570574
sess,
571575
modules,
572576
&cg_args.linker_opts,
577+
outputs,
573578
disambiguated_crate_name_for_dumps,
574579
);
575580

crates/rustc_codegen_spirv/src/linker/mod.rs

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use rspirv::dr::{Block, Instruction, Loader, Module, ModuleHeader, Operand};
2727
use rspirv::spirv::{Op, StorageClass, Word};
2828
use rustc_data_structures::fx::FxHashMap;
2929
use rustc_errors::ErrorGuaranteed;
30+
use rustc_session::config::OutputFilenames;
3031
use rustc_session::Session;
3132
use std::collections::BTreeMap;
3233
use std::ffi::{OsStr, OsString};
@@ -151,6 +152,7 @@ pub fn link(
151152
sess: &Session,
152153
mut inputs: Vec<Module>,
153154
opts: &Options,
155+
outputs: &OutputFilenames,
154156
disambiguated_crate_name_for_dumps: &OsStr,
155157
) -> Result<LinkResult> {
156158
let mut output = {
@@ -383,24 +385,34 @@ pub fn link(
383385
}
384386
};
385387

388+
let spv_words;
386389
let spv_bytes = {
387390
let _timer = sess.timer("assemble-to-spv_bytes-for-spirt");
388-
spirv_tools::binary::from_binary(&output.assemble()).to_vec()
391+
spv_words = output.assemble();
392+
// FIXME(eddyb) this is wastefully cloning all the bytes, but also
393+
// `spirt::Module` should have a method that takes `Vec<u32>`.
394+
spirv_tools::binary::from_binary(&spv_words).to_vec()
389395
};
390396
let cx = std::rc::Rc::new(spirt::Context::new());
391397
let mut module = {
392398
let _timer = sess.timer("spirt::Module::lower_from_spv_file");
393399
match spirt::Module::lower_from_spv_bytes(cx.clone(), spv_bytes) {
394400
Ok(module) => module,
395401
Err(e) => {
396-
use rspirv::binary::Disassemble;
402+
let spv_path = outputs.temp_path_ext("spirt-lower-from-spv-input.spv", None);
403+
404+
let was_saved_msg = match std::fs::write(
405+
&spv_path,
406+
spirv_tools::binary::from_binary(&spv_words),
407+
) {
408+
Ok(()) => format!("was saved to {}", spv_path.display()),
409+
Err(e) => format!("could not be saved: {e}"),
410+
};
397411

398412
return Err(sess
399413
.struct_err(format!("{e}"))
400-
.note(format!(
401-
"while lowering this SPIR-V module to SPIR-T:\n{}",
402-
output.disassemble()
403-
))
414+
.note("while lowering SPIR-V module to SPIR-T (spirt::spv::lower)")
415+
.note(format!("input SPIR-V module {was_saved_msg}"))
404416
.emit());
405417
}
406418
}
@@ -438,14 +450,30 @@ pub fn link(
438450
let _timer = sess.timer("spirt_passes::diagnostics::report_diagnostics");
439451
spirt_passes::diagnostics::report_diagnostics(sess, opts, &module)
440452
};
453+
let any_spirt_bugs = report_diagnostics_result
454+
.as_ref()
455+
.err()
456+
.map_or(false, |e| e.any_errors_were_spirt_bugs);
441457

442-
// NOTE(eddyb) this should be *before* `lift_to_spv` below,
443-
// so if that fails, the dump could be used to debug it.
444-
if let Some(dump_dir) = &opts.dump_spirt_passes {
445-
let dump_spirt_file_path = dump_dir
458+
let mut dump_spirt_file_path = opts.dump_spirt_passes.as_ref().map(|dump_dir| {
459+
dump_dir
446460
.join(disambiguated_crate_name_for_dumps)
447-
.with_extension("spirt");
461+
.with_extension("spirt")
462+
});
463+
464+
// FIXME(eddyb) this won't allow seeing the individual passes, but it's
465+
// better than nothing (we could theoretically put this whole block in
466+
// a loop so that we redo everything but keeping `Module` clones?).
467+
if any_spirt_bugs && dump_spirt_file_path.is_none() {
468+
if per_pass_module_for_dumping.is_empty() {
469+
per_pass_module_for_dumping.push(("", module.clone()));
470+
}
471+
dump_spirt_file_path = Some(outputs.temp_path_ext("spirt", None));
472+
}
448473

474+
// NOTE(eddyb) this should be *before* `lift_to_spv` below,
475+
// so if that fails, the dump could be used to debug it.
476+
if let Some(dump_spirt_file_path) = &dump_spirt_file_path {
449477
// HACK(eddyb) unless requested otherwise, clean up the pretty-printed
450478
// SPIR-T output by converting our custom extended instructions, to
451479
// standard SPIR-V debuginfo (which SPIR-T knows how to pretty-print).
@@ -464,7 +492,7 @@ pub fn link(
464492
let pretty = plan.pretty_print();
465493

466494
// FIXME(eddyb) don't allocate whole `String`s here.
467-
std::fs::write(&dump_spirt_file_path, pretty.to_string()).unwrap();
495+
std::fs::write(dump_spirt_file_path, pretty.to_string()).unwrap();
468496
std::fs::write(
469497
dump_spirt_file_path.with_extension("spirt.html"),
470498
pretty
@@ -475,6 +503,19 @@ pub fn link(
475503
.unwrap();
476504
}
477505

506+
if any_spirt_bugs {
507+
let mut note = sess.struct_note_without_error("SPIR-T bugs were reported");
508+
note.help(format!(
509+
"pretty-printed SPIR-T was saved to {}.html",
510+
dump_spirt_file_path.as_ref().unwrap().display()
511+
));
512+
if opts.dump_spirt_passes.is_none() {
513+
note.help("re-run with `RUSTGPU_CODEGEN_ARGS=\"--dump-spirt-passes=$PWD\"` for more details");
514+
}
515+
note.note("pretty-printed SPIR-T is preferred when reporting Rust-GPU issues")
516+
.emit();
517+
}
518+
478519
// NOTE(eddyb) this is late so that `--dump-spirt-passes` is processed,
479520
// even/especially when errors were reported, but lifting to SPIR-V is
480521
// skipped (since it could very well fail due to reported errors).

crates/rustc_codegen_spirv/src/linker/spirt_passes/diagnostics.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,22 @@ use spirt::{
1717
use std::marker::PhantomData;
1818
use std::{mem, str};
1919

20+
pub(crate) struct ReportedDiagnostics {
21+
pub rustc_errors_guarantee: rustc_errors::ErrorGuaranteed,
22+
pub any_errors_were_spirt_bugs: bool,
23+
}
24+
25+
impl From<ReportedDiagnostics> for rustc_errors::ErrorGuaranteed {
26+
fn from(r: ReportedDiagnostics) -> Self {
27+
r.rustc_errors_guarantee
28+
}
29+
}
30+
2031
pub(crate) fn report_diagnostics(
2132
sess: &Session,
2233
linker_options: &crate::linker::Options,
2334
module: &Module,
24-
) -> crate::linker::Result<()> {
35+
) -> Result<(), ReportedDiagnostics> {
2536
let cx = &module.cx();
2637

2738
let mut reporter = DiagnosticReporter {
@@ -68,28 +79,12 @@ pub(crate) fn report_diagnostics(
6879
exportee.inner_visit_with(&mut reporter);
6980
}
7081

71-
if reporter.any_spirt_bugs {
72-
let mut note = sess.struct_note_without_error("SPIR-T bugs were reported");
73-
match &linker_options.dump_spirt_passes {
74-
Some(dump_dir) => {
75-
note.help(format!(
76-
"pretty-printed SPIR-T will be saved to `{}`, as `.spirt.html` files",
77-
dump_dir.display()
78-
));
79-
}
80-
None => {
81-
// FIXME(eddyb) maybe just always generate the files in a tmpdir?
82-
note.help(
83-
"re-run with `RUSTGPU_CODEGEN_ARGS=\"--dump-spirt-passes=$PWD\"` to \
84-
get pretty-printed SPIR-T (`.spirt.html`)",
85-
);
86-
}
87-
}
88-
note.note("pretty-printed SPIR-T is preferred when reporting Rust-GPU issues")
89-
.emit();
90-
}
91-
92-
reporter.overall_result
82+
reporter
83+
.overall_result
84+
.map_err(|rustc_errors_guarantee| ReportedDiagnostics {
85+
rustc_errors_guarantee,
86+
any_errors_were_spirt_bugs: reporter.any_spirt_bugs,
87+
})
9388
}
9489

9590
// HACK(eddyb) version of `decorations::LazilyDecoded` that works for SPIR-T.

crates/rustc_codegen_spirv/src/linker/test.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use super::{link, LinkResult};
22
use pipe::pipe;
33
use rspirv::dr::{Loader, Module};
44
use rustc_errors::{registry::Registry, TerminalUrl};
5-
use rustc_session::{config::Input, CompilerIO};
5+
use rustc_session::config::{Input, OutputFilenames, OutputTypes};
6+
use rustc_session::CompilerIO;
67
use rustc_span::FileName;
78
use std::io::Read;
89

@@ -148,7 +149,20 @@ fn link_with_linker_opts(
148149
)
149150
};
150151

151-
let res = link(&sess, modules, opts, Default::default());
152+
let res = link(
153+
&sess,
154+
modules,
155+
opts,
156+
&OutputFilenames::new(
157+
std::env::current_dir().unwrap_or_default(),
158+
"".into(),
159+
None,
160+
None,
161+
"".into(),
162+
OutputTypes::new(&[]),
163+
),
164+
Default::default(),
165+
);
152166
assert_eq!(sess.has_errors(), res.as_ref().err().copied());
153167
res.map(|res| match res {
154168
LinkResult::SingleModule(m) => *m,

0 commit comments

Comments
 (0)