@@ -27,6 +27,7 @@ use rspirv::dr::{Block, Instruction, Loader, Module, ModuleHeader, Operand};
27
27
use rspirv:: spirv:: { Op , StorageClass , Word } ;
28
28
use rustc_data_structures:: fx:: FxHashMap ;
29
29
use rustc_errors:: ErrorGuaranteed ;
30
+ use rustc_session:: config:: OutputFilenames ;
30
31
use rustc_session:: Session ;
31
32
use std:: collections:: BTreeMap ;
32
33
use std:: ffi:: { OsStr , OsString } ;
@@ -151,6 +152,7 @@ pub fn link(
151
152
sess : & Session ,
152
153
mut inputs : Vec < Module > ,
153
154
opts : & Options ,
155
+ outputs : & OutputFilenames ,
154
156
disambiguated_crate_name_for_dumps : & OsStr ,
155
157
) -> Result < LinkResult > {
156
158
let mut output = {
@@ -383,24 +385,34 @@ pub fn link(
383
385
}
384
386
} ;
385
387
388
+ let spv_words;
386
389
let spv_bytes = {
387
390
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 ( )
389
395
} ;
390
396
let cx = std:: rc:: Rc :: new ( spirt:: Context :: new ( ) ) ;
391
397
let mut module = {
392
398
let _timer = sess. timer ( "spirt::Module::lower_from_spv_file" ) ;
393
399
match spirt:: Module :: lower_from_spv_bytes ( cx. clone ( ) , spv_bytes) {
394
400
Ok ( module) => module,
395
401
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
+ } ;
397
411
398
412
return Err ( sess
399
413
. 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}" ) )
404
416
. emit ( ) ) ;
405
417
}
406
418
}
@@ -438,14 +450,30 @@ pub fn link(
438
450
let _timer = sess. timer ( "spirt_passes::diagnostics::report_diagnostics" ) ;
439
451
spirt_passes:: diagnostics:: report_diagnostics ( sess, opts, & module)
440
452
} ;
453
+ let any_spirt_bugs = report_diagnostics_result
454
+ . as_ref ( )
455
+ . err ( )
456
+ . map_or ( false , |e| e. any_errors_were_spirt_bugs ) ;
441
457
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
446
460
. 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
+ }
448
473
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 {
449
477
// HACK(eddyb) unless requested otherwise, clean up the pretty-printed
450
478
// SPIR-T output by converting our custom extended instructions, to
451
479
// standard SPIR-V debuginfo (which SPIR-T knows how to pretty-print).
@@ -464,7 +492,7 @@ pub fn link(
464
492
let pretty = plan. pretty_print ( ) ;
465
493
466
494
// 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 ( ) ;
468
496
std:: fs:: write (
469
497
dump_spirt_file_path. with_extension ( "spirt.html" ) ,
470
498
pretty
@@ -475,6 +503,19 @@ pub fn link(
475
503
. unwrap ( ) ;
476
504
}
477
505
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
+
478
519
// NOTE(eddyb) this is late so that `--dump-spirt-passes` is processed,
479
520
// even/especially when errors were reported, but lifting to SPIR-V is
480
521
// skipped (since it could very well fail due to reported errors).
0 commit comments