From 314f068d6bad8a1d25c0f7c6f4ba0893f9fcc631 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 11 Aug 2025 19:11:00 +0300 Subject: [PATCH 1/5] Try dumping, when a SPIR-T pass panics, its in-progress `spirt::Module` state. --- crates/rustc_codegen_spirv/src/linker/mod.rs | 182 ++++++++++++------ .../src/linker/spirt_passes/mod.rs | 10 +- 2 files changed, 128 insertions(+), 64 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/linker/mod.rs b/crates/rustc_codegen_spirv/src/linker/mod.rs index ddde1561f2..de40a19131 100644 --- a/crates/rustc_codegen_spirv/src/linker/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/mod.rs @@ -29,6 +29,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_session::Session; use rustc_session::config::OutputFilenames; +use std::cell::Cell; use std::collections::BTreeMap; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -502,42 +503,53 @@ pub fn link( module, per_pass_module_for_dumping: vec![], + in_progress_pass_name: Cell::new(Some("lower_from_spv")), any_spirt_bugs: false, }; let module = &mut *dump_guard.module; - // FIXME(eddyb) set the name into `dump_guard` to be able to access it on panic. - let before_pass = |pass| sess.timer(pass); - let mut after_pass = |pass, module: &spirt::Module, timer| { + // FIXME(eddyb) consider returning a `Drop`-implementing type instead? + let before_pass = |pass_name| { + let outer_pass_name = dump_guard.in_progress_pass_name.replace(Some(pass_name)); + + // FIXME(eddyb) could it make sense to allow these to nest? + assert_eq!(outer_pass_name, None); + + sess.timer(pass_name) + }; + let mut after_pass = |module: Option<&spirt::Module>, timer| { drop(timer); - if opts.dump_spirt_passes.is_some() { + let pass_name = dump_guard.in_progress_pass_name.take().unwrap(); + if let Some(module) = module + && opts.dump_spirt_passes.is_some() + { dump_guard .per_pass_module_for_dumping - .push((pass, module.clone())); + .push((pass_name.into(), module.clone())); } }; // HACK(eddyb) don't dump the unstructured state if not requested, as // after SPIR-T 0.4.0 it's extremely verbose (due to def-use hermeticity). - if opts.spirt_keep_unstructured_cfg_in_dumps || !opts.structurize { - after_pass("lower_from_spv", module, lower_from_spv_timer); - } else { - drop(lower_from_spv_timer); - } + after_pass( + (opts.spirt_keep_unstructured_cfg_in_dumps || !opts.structurize).then_some(&*module), + lower_from_spv_timer, + ); // NOTE(eddyb) this *must* run on unstructured CFGs, to do its job. // FIXME(eddyb) no longer relying on structurization, try porting this // to replace custom aborts in `Block`s and inject `ExitInvocation`s // after them (truncating the `Block` and/or parent region if necessary). { - let _timer = before_pass( + let timer = before_pass( "spirt_passes::controlflow::convert_custom_aborts_to_unstructured_returns_in_entry_points", ); spirt_passes::controlflow::convert_custom_aborts_to_unstructured_returns_in_entry_points(opts, module); + after_pass(None, timer); } if opts.structurize { let timer = before_pass("spirt::legalize::structurize_func_cfgs"); spirt::passes::legalize::structurize_func_cfgs(module); - after_pass("structurize_func_cfgs", module, timer); + after_pass(Some(module), timer); } if !opts.spirt_passes.is_empty() { @@ -553,11 +565,11 @@ pub fn link( { let timer = before_pass("spirt_passes::validate"); spirt_passes::validate::validate(module); - after_pass("validate", module, timer); + after_pass(Some(module), timer); } { - let _timer = before_pass("spirt_passes::diagnostics::report_diagnostics"); + let timer = before_pass("spirt_passes::diagnostics::report_diagnostics"); spirt_passes::diagnostics::report_diagnostics(sess, opts, module).map_err( |spirt_passes::diagnostics::ReportedDiagnostics { rustc_errors_guarantee, @@ -567,17 +579,21 @@ pub fn link( rustc_errors_guarantee }, )?; + after_pass(None, timer); } // Replace our custom debuginfo instructions just before lifting to SPIR-V. { - let _timer = before_pass("spirt_passes::debuginfo::convert_custom_debuginfo_to_spv"); + let timer = before_pass("spirt_passes::debuginfo::convert_custom_debuginfo_to_spv"); spirt_passes::debuginfo::convert_custom_debuginfo_to_spv(module); + after_pass(None, timer); } let spv_words = { - let _timer = before_pass("spirt::Module::lift_to_spv_module_emitter"); - module.lift_to_spv_module_emitter().unwrap().words + let timer = before_pass("spirt::Module::lift_to_spv_module_emitter"); + let spv_words = module.lift_to_spv_module_emitter().unwrap().words; + after_pass(None, timer); + spv_words }; // FIXME(eddyb) dump both SPIR-T and `spv_words` if there's an error here. output = { @@ -756,13 +772,26 @@ struct SpirtDumpGuard<'a> { disambiguated_crate_name_for_dumps: &'a OsStr, module: &'a mut spirt::Module, - per_pass_module_for_dumping: Vec<(&'static str, spirt::Module)>, + per_pass_module_for_dumping: Vec<(Cow<'static, str>, spirt::Module)>, + in_progress_pass_name: Cell>, any_spirt_bugs: bool, } impl Drop for SpirtDumpGuard<'_> { fn drop(&mut self) { - self.any_spirt_bugs |= std::thread::panicking(); + if std::thread::panicking() { + self.any_spirt_bugs = true; + + // HACK(eddyb) the active pass panicked, make sure to include its + // (potentially corrupted) state, which will hopefully be printed + // later below (with protection against panicking during printing). + if let Some(pass_name) = self.in_progress_pass_name.get() { + self.per_pass_module_for_dumping.push(( + format!("{pass_name} [PANICKED]").into(), + self.module.clone(), + )); + } + } let mut dump_spirt_file_path = self.linker_options @@ -782,55 +811,90 @@ impl Drop for SpirtDumpGuard<'_> { 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(("", self.module.clone())); + .push(("".into(), self.module.clone())); } dump_spirt_file_path = Some(self.outputs.temp_path_for_diagnostic("spirt")); } - if let Some(dump_spirt_file_path) = &dump_spirt_file_path { - for (_, module) in &mut self.per_pass_module_for_dumping { - self.linker_options.spirt_cleanup_for_dumping(module); - } + let Some(dump_spirt_file_path) = &dump_spirt_file_path else { + return; + }; + + 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); + } - // FIXME(eddyb) catch panics during pretty-printing itself, and - // tell the user to use `--dump-spirt-passes` (and resolve the - // second FIXME below so it does anything) - also, that may need + let cx = self.module.cx(); + let versions = self + .per_pass_module_for_dumping + .iter() + .map(|(pass_name, module)| (format!("after {pass_name}"), module)); + + let mut panicked_printing_after_passes = None; + for truncate_version_count in (1..=versions.len()).rev() { + // FIXME(eddyb) tell the user to use `--dump-spirt-passes` if that + // wasn't active but a panic happens - on top of that, it may need // quieting the panic handler, likely controlled by a `thread_local!` - // (while the panic handler is global), but that would be useful + // (while the panic handler is global), and that would also be useful // for collecting a panic message (assuming any of this is worth it). - // FIXME(eddyb) when per-pass versions are available, try building - // plans for individual versions, or maybe repeat `Plan::for_versions` - // without the last version if it initially panicked? - let plan = spirt::print::Plan::for_versions( - self.module.cx_ref(), - self.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_spirt_file_path, pretty.to_string()).unwrap(); - std::fs::write( - dump_spirt_file_path.with_extension("spirt.html"), - pretty - .render_to_html() - .with_dark_mode_support() - .to_html_doc(), - ) - .unwrap(); - if self.any_spirt_bugs { - let mut note = self.sess.dcx().struct_note("SPIR-T bugs were encountered"); - note.help(format!( - "pretty-printed SPIR-T was saved to {}.html", - dump_spirt_file_path.display() - )); - if self.linker_options.dump_spirt_passes.is_none() { - note.help("re-run with `RUSTGPU_CODEGEN_ARGS=\"--dump-spirt-passes=$PWD\"` for more details"); + // HACK(eddyb) for now, keeping the panic handler works out, as the + // panic messages would at least be seen by the user. + let printed_or_panicked = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let pretty = spirt::print::Plan::for_versions( + &cx, + versions.clone().take(truncate_version_count), + ) + .pretty_print(); + + // FIXME(eddyb) don't allocate whole `String`s here. + std::fs::write(dump_spirt_file_path, pretty.to_string()).unwrap(); + std::fs::write( + dump_spirt_file_path.with_extension("spirt.html"), + pretty + .render_to_html() + .with_dark_mode_support() + .to_html_doc(), + ) + .unwrap(); + })); + match printed_or_panicked { + Ok(()) => { + if truncate_version_count != versions.len() { + panicked_printing_after_passes = Some( + self.per_pass_module_for_dumping[truncate_version_count..] + .iter() + .map(|(pass_name, _)| format!("`{pass_name}`")) + .collect::>() + .join(", "), + ); + } + break; + } + Err(panic) => { + if truncate_version_count == 1 { + std::panic::resume_unwind(panic); + } } - note.note("pretty-printed SPIR-T is preferred when reporting Rust-GPU issues"); - note.emit(); } } + if self.any_spirt_bugs || panicked_printing_after_passes.is_some() { + let mut note = self.sess.dcx().struct_note("SPIR-T bugs were encountered"); + if let Some(pass_names) = panicked_printing_after_passes { + note.warn(format!( + "SPIR-T pretty-printing panicked after: {pass_names}" + )); + } + note.help(format!( + "pretty-printed SPIR-T was saved to {}.html", + dump_spirt_file_path.display() + )); + if self.linker_options.dump_spirt_passes.is_none() { + 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"); + note.emit(); + } } } diff --git a/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs b/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs index ce52895cba..f8b8c8518a 100644 --- a/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs @@ -120,7 +120,7 @@ pub(super) fn run_func_passes

( passes: &[impl AsRef], // FIXME(eddyb) this is a very poor approximation of a "profiler" abstraction. mut before_pass: impl FnMut(&'static str, &Module) -> P, - mut after_pass: impl FnMut(&'static str, &Module, P), + mut after_pass: impl FnMut(Option<&Module>, P), ) { let cx = &module.cx(); @@ -156,15 +156,15 @@ pub(super) fn run_func_passes

( let profiler = before_pass("qptr::lower_from_spv_ptrs", module); spirt::passes::qptr::lower_from_spv_ptrs(module, layout_config); - after_pass("qptr::lower_from_spv_ptrs", module, profiler); + after_pass(Some(module), profiler); let profiler = before_pass("qptr::analyze_uses", module); spirt::passes::qptr::analyze_uses(module, layout_config); - after_pass("qptr::analyze_uses", module, profiler); + after_pass(Some(module), profiler); let profiler = before_pass("qptr::lift_to_spv_ptrs", module); spirt::passes::qptr::lift_to_spv_ptrs(module, layout_config); - after_pass("qptr::lift_to_spv_ptrs", module, profiler); + after_pass(Some(module), profiler); continue; } @@ -187,7 +187,7 @@ pub(super) fn run_func_passes

( remove_unused_values_in_func(cx, func_def_body); } } - after_pass(full_name, module, profiler); + after_pass(Some(module), profiler); } } From 5046d57a3e854d80ee3e0ccae91a2d1c6373ca54 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 24 Jul 2025 11:36:11 +0300 Subject: [PATCH 2/5] Replace `SpirvValueKind::IllegalTypeUsed` with mere `undef`. --- .../src/builder/builder_methods.rs | 5 +---- .../src/builder/byte_addressable_buffer.rs | 10 +++------- crates/rustc_codegen_spirv/src/builder_spirv.rs | 17 ----------------- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 30fc1ae6c9..bcd2939958 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -4110,10 +4110,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { self.codegen_buffer_store_intrinsic(args, mode); let void_ty = SpirvType::Void.def(rustc_span::DUMMY_SP, self); - return SpirvValue { - kind: SpirvValueKind::IllegalTypeUsed(void_ty), - ty: void_ty, - }; + return self.undef(void_ty); } if let Some((source_ty, target_ty)) = from_trait_impl { diff --git a/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs b/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs index ab2a78cf65..60f0109573 100644 --- a/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs +++ b/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs @@ -2,7 +2,7 @@ use crate::maybe_pqp_cg_ssa as rustc_codegen_ssa; use super::Builder; -use crate::builder_spirv::{SpirvValue, SpirvValueExt, SpirvValueKind}; +use crate::builder_spirv::{SpirvValue, SpirvValueExt}; use crate::spirv_type::SpirvType; use rspirv::spirv::{Decoration, Word}; use rustc_abi::{Align, Size}; @@ -186,12 +186,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pass_mode: &PassMode, ) -> SpirvValue { match pass_mode { - PassMode::Ignore => { - return SpirvValue { - kind: SpirvValueKind::IllegalTypeUsed(result_type), - ty: result_type, - }; - } + PassMode::Ignore => return self.undef(result_type), + // PassMode::Pair is identical to PassMode::Direct - it's returned as a struct PassMode::Direct(_) | PassMode::Pair(_, _) => (), PassMode::Cast { .. } => { diff --git a/crates/rustc_codegen_spirv/src/builder_spirv.rs b/crates/rustc_codegen_spirv/src/builder_spirv.rs index e05b433057..fc735a4178 100644 --- a/crates/rustc_codegen_spirv/src/builder_spirv.rs +++ b/crates/rustc_codegen_spirv/src/builder_spirv.rs @@ -40,13 +40,6 @@ pub enum SpirvValueKind { /// of such constants, instead of where they're generated (and cached). IllegalConst(Word), - /// This can only happen in one specific case - which is as a result of - /// `codegen_buffer_store_intrinsic`, that function is supposed to return - /// `OpTypeVoid`, however because it gets inline by the compiler it can't. - /// Instead we return this, and trigger an error if we ever end up using the - /// result of this function call (which we can't). - IllegalTypeUsed(Word), - // FIXME(eddyb) this shouldn't be needed, but `rustc_codegen_ssa` still relies // on converting `Function`s to `Value`s even for direct calls, the `Builder` // should just have direct and indirect `call` variants (or a `Callee` enum). @@ -166,16 +159,6 @@ impl SpirvValue { id } - SpirvValueKind::IllegalTypeUsed(id) => { - cx.tcx - .dcx() - .struct_span_err(span, "Can't use type as a value") - .with_note(format!("Type: *{}", cx.debug_type(id))) - .emit(); - - id - } - SpirvValueKind::FnAddr { .. } => { cx.builder .const_to_id From 77f28e1f25e4b67e27f64740e9ea6b8ba9a1a1f3 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 24 Jul 2025 15:08:41 +0300 Subject: [PATCH 3/5] Always register zombie messages, only at most defer their `Span`s. --- .../src/builder/builder_methods.rs | 16 ++- .../rustc_codegen_spirv/src/builder_spirv.rs | 98 +++++++++---------- .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 17 +++- .../ui/dis/ptr_copy.normal.stderr | 31 +++++- 4 files changed, 103 insertions(+), 59 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index bcd2939958..e48bb651b2 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -2448,11 +2448,25 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { ); // Defer the cast so that it has a chance to be avoided. let original_ptr = ptr.def(self); + let bitcast_result_id = self.emit().bitcast(dest_ty, None, original_ptr).unwrap(); + + self.zombie( + bitcast_result_id, + &format!( + "cannot cast between pointer types\ + \nfrom `{}`\ + \n to `{}`", + self.debug_type(ptr.ty), + self.debug_type(dest_ty) + ), + ); + SpirvValue { + zombie_waiting_for_span: false, kind: SpirvValueKind::LogicalPtrCast { original_ptr, original_ptr_ty: ptr.ty, - bitcast_result_id: self.emit().bitcast(dest_ty, None, original_ptr).unwrap(), + bitcast_result_id, }, ty: dest_ty, } diff --git a/crates/rustc_codegen_spirv/src/builder_spirv.rs b/crates/rustc_codegen_spirv/src/builder_spirv.rs index fc735a4178..d4d7627725 100644 --- a/crates/rustc_codegen_spirv/src/builder_spirv.rs +++ b/crates/rustc_codegen_spirv/src/builder_spirv.rs @@ -70,6 +70,13 @@ pub enum SpirvValueKind { #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct SpirvValue { + // HACK(eddyb) used to cheaply check whether this is a SPIR-V value ID + // with a "zombie" (deferred error) attached to it, that may need a `Span` + // still (e.g. such as constants, which can't easily take a `Span`). + // FIXME(eddyb) a whole `bool` field is sadly inefficient, but anything + // which may make `SpirvValue` smaller requires far too much impl effort. + pub zombie_waiting_for_span: bool, + pub kind: SpirvValueKind, pub ty: Word, } @@ -103,7 +110,11 @@ impl SpirvValue { } else { SpirvValueKind::IllegalConst(pointee) }; - Some(SpirvValue { kind, ty }) + Some(SpirvValue { + zombie_waiting_for_span: entry.legal.is_err(), + kind, + ty, + }) } _ => None, } @@ -127,38 +138,7 @@ impl SpirvValue { } pub fn def_with_span(self, cx: &CodegenCx<'_>, span: Span) -> Word { - match self.kind { - SpirvValueKind::Def(id) => id, - - SpirvValueKind::IllegalConst(id) => { - let entry = &cx.builder.id_to_const.borrow()[&id]; - let msg = match entry.legal.unwrap_err() { - IllegalConst::Shallow(cause) => { - if let ( - LeafIllegalConst::CompositeContainsPtrTo, - SpirvConst::Composite(_fields), - ) = (cause, &entry.val) - { - // FIXME(eddyb) materialize this at runtime, using - // `OpCompositeConstruct` (transitively, i.e. after - // putting every field through `SpirvValue::def`), - // if we have a `Builder` to do that in. - // FIXME(eddyb) this isn't possible right now, as - // the builder would be dynamically "locked" anyway - // (i.e. attempting to do `bx.emit()` would panic). - } - - cause.message() - } - - IllegalConst::Indirect(cause) => cause.message(), - }; - - cx.zombie_with_span(id, span, msg); - - id - } - + let id = match self.kind { SpirvValueKind::FnAddr { .. } => { cx.builder .const_to_id @@ -171,26 +151,18 @@ impl SpirvValue { .val } - SpirvValueKind::LogicalPtrCast { + SpirvValueKind::Def(id) + | SpirvValueKind::IllegalConst(id) + | SpirvValueKind::LogicalPtrCast { original_ptr: _, - original_ptr_ty, - bitcast_result_id, - } => { - cx.zombie_with_span( - bitcast_result_id, - span, - &format!( - "cannot cast between pointer types\ - \nfrom `{}`\ - \n to `{}`", - cx.debug_type(original_ptr_ty), - cx.debug_type(self.ty) - ), - ); - - bitcast_result_id - } + original_ptr_ty: _, + bitcast_result_id: id, + } => id, + }; + if self.zombie_waiting_for_span { + cx.add_span_to_zombie_if_missing(id, span); } + id } } @@ -201,6 +173,7 @@ pub trait SpirvValueExt { impl SpirvValueExt for Word { fn with_type(self, ty: Word) -> SpirvValue { SpirvValue { + zombie_waiting_for_span: false, kind: SpirvValueKind::Def(self), ty, } @@ -606,7 +579,11 @@ impl<'tcx> BuilderSpirv<'tcx> { } else { SpirvValueKind::IllegalConst(entry.val) }; - return SpirvValue { kind, ty }; + return SpirvValue { + zombie_waiting_for_span: entry.legal.is_err(), + kind, + ty, + }; } let val = val_with_type.val; @@ -783,6 +760,17 @@ impl<'tcx> BuilderSpirv<'tcx> { LeafIllegalConst::UntypedConstDataFromAlloc, )), }; + + // FIXME(eddyb) avoid dragging "const (il)legality" around, as well + // (sadly that does require that `SpirvConst` -> SPIR-V be injective, + // e.g. `OpUndef` can never be used for unrepresentable constants). + if let Err(illegal) = legal { + let msg = match illegal { + IllegalConst::Shallow(cause) | IllegalConst::Indirect(cause) => cause.message(), + }; + cx.zombie_no_span(id, msg); + } + let val = val.tcx_arena_alloc_slices(cx); assert_matches!( self.const_to_id @@ -802,7 +790,11 @@ impl<'tcx> BuilderSpirv<'tcx> { } else { SpirvValueKind::IllegalConst(id) }; - SpirvValue { kind, ty } + SpirvValue { + zombie_waiting_for_span: legal.is_err(), + kind, + ty, + } } pub fn lookup_const_by_id(&self, id: Word) -> Option> { diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index f3811f8a19..ffec195a21 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -245,9 +245,9 @@ impl<'tcx> CodegenCx<'tcx> { /// is stripped from the binary. /// /// Errors will only be emitted (by `linker::zombies`) for reachable zombies. - pub fn zombie_with_span(&self, word: Word, span: Span, reason: &str) { + pub fn zombie_with_span(&self, id: Word, span: Span, reason: &str) { self.zombie_decorations.borrow_mut().insert( - word, + id, ( ZombieDecoration { // FIXME(eddyb) this could take advantage of `Cow` and use @@ -258,8 +258,16 @@ impl<'tcx> CodegenCx<'tcx> { ), ); } - pub fn zombie_no_span(&self, word: Word, reason: &str) { - self.zombie_with_span(word, DUMMY_SP, reason); + pub fn zombie_no_span(&self, id: Word, reason: &str) { + self.zombie_with_span(id, DUMMY_SP, reason); + } + + pub fn add_span_to_zombie_if_missing(&self, id: Word, span: Span) { + if span != DUMMY_SP + && let Some((_, src_loc @ None)) = self.zombie_decorations.borrow_mut().get_mut(&id) + { + *src_loc = SrcLocDecoration::from_rustc_span(span, &self.builder); + } } pub fn finalize_module(self) -> Module { @@ -849,6 +857,7 @@ impl<'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'tcx> { self.def_constant(ty, SpirvConst::ZombieUndefForFnAddr); SpirvValue { + zombie_waiting_for_span: false, kind: SpirvValueKind::FnAddr { function: function.id, }, diff --git a/tests/compiletests/ui/dis/ptr_copy.normal.stderr b/tests/compiletests/ui/dis/ptr_copy.normal.stderr index c7db2ddf11..b993618ede 100644 --- a/tests/compiletests/ui/dis/ptr_copy.normal.stderr +++ b/tests/compiletests/ui/dis/ptr_copy.normal.stderr @@ -28,6 +28,12 @@ note: called by `main` error: cannot cast between pointer types from `*f32` to `*struct () { }` + --> $CORE_SRC/ptr/mod.rs:625:34 + | +625 | src: *const () = src as *const (), + | ^^^^^^^^^^^^^^^^ + | +note: used from within `core::ptr::copy::` --> $CORE_SRC/ptr/mod.rs:621:9 | 621 | / ub_checks::assert_unsafe_precondition!( @@ -37,6 +43,29 @@ error: cannot cast between pointer types 631 | | && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) 632 | | ); | |_________^ +note: called by `ptr_copy::copy_via_raw_ptr` + --> $DIR/ptr_copy.rs:28:18 + | +28 | unsafe { core::ptr::copy(src, dst, 1) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: called by `ptr_copy::main` + --> $DIR/ptr_copy.rs:33:5 + | +33 | copy_via_raw_ptr(&i, o); + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: called by `main` + --> $DIR/ptr_copy.rs:32:8 + | +32 | pub fn main(i: f32, o: &mut f32) { + | ^^^^ + +error: cannot cast between pointer types + from `*f32` + to `*struct () { }` + --> $CORE_SRC/ptr/mod.rs:626:32 + | +626 | dst: *mut () = dst as *mut (), + | ^^^^^^^^^^^^^^ | note: used from within `core::ptr::copy::` --> $CORE_SRC/ptr/mod.rs:621:9 @@ -64,5 +93,5 @@ note: called by `main` 32 | pub fn main(i: f32, o: &mut f32) { | ^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors From 90620c78ee9fbbd7449255478cd2f5c41e5ce2e7 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 24 Jul 2025 16:34:49 +0300 Subject: [PATCH 4/5] Remove `SpirvValueKind::IllegalConst`. --- .../src/builder/builder_methods.rs | 20 +++++++---- .../rustc_codegen_spirv/src/builder_spirv.rs | 36 +++---------------- .../src/codegen_cx/constant.rs | 5 ++- 3 files changed, 20 insertions(+), 41 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index e48bb651b2..30c7d7f391 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -2381,13 +2381,6 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { #[instrument(level = "trace", skip(self), fields(ptr, ptr_ty = ?self.debug_type(ptr.ty), dest_ty = ?self.debug_type(dest_ty)))] fn pointercast(&mut self, ptr: Self::Value, dest_ty: Self::Type) -> Self::Value { - // HACK(eddyb) reuse the special-casing in `const_bitcast`, which relies - // on adding a pointer type to an untyped pointer (to some const data). - if let SpirvValueKind::IllegalConst(_) = ptr.kind { - trace!("illegal const"); - return self.const_bitcast(ptr, dest_ty); - } - if ptr.ty == dest_ty { trace!("ptr.ty == dest_ty"); return ptr; @@ -2446,6 +2439,19 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { self.debug_type(ptr_pointee), self.debug_type(dest_pointee), ); + + // HACK(eddyb) reuse the special-casing in `const_bitcast`, which relies + // on adding a pointer type to an untyped pointer (to some const data). + if self.builder.lookup_const(ptr).is_some() { + // FIXME(eddyb) remove the condition on `zombie_waiting_for_span`, + // and constant-fold all pointer bitcasts, regardless of "legality", + // once `strip_ptrcasts` can undo `const_bitcast`, as well. + if ptr.zombie_waiting_for_span { + trace!("illegal const"); + return self.const_bitcast(ptr, dest_ty); + } + } + // Defer the cast so that it has a chance to be avoided. let original_ptr = ptr.def(self); let bitcast_result_id = self.emit().bitcast(dest_ty, None, original_ptr).unwrap(); diff --git a/crates/rustc_codegen_spirv/src/builder_spirv.rs b/crates/rustc_codegen_spirv/src/builder_spirv.rs index d4d7627725..ecb97b792e 100644 --- a/crates/rustc_codegen_spirv/src/builder_spirv.rs +++ b/crates/rustc_codegen_spirv/src/builder_spirv.rs @@ -35,11 +35,6 @@ use std::{fs::File, io::Write, path::Path}; pub enum SpirvValueKind { Def(Word), - /// The ID of a global instruction matching a `SpirvConst`, but which cannot - /// pass validation. Used to error (or attach zombie spans), at the usesites - /// of such constants, instead of where they're generated (and cached). - IllegalConst(Word), - // FIXME(eddyb) this shouldn't be needed, but `rustc_codegen_ssa` still relies // on converting `Function`s to `Value`s even for direct calls, the `Builder` // should just have direct and indirect `call` variants (or a `Callee` enum). @@ -96,7 +91,7 @@ impl SpirvValue { pub fn const_fold_load(self, cx: &CodegenCx<'_>) -> Option { match self.kind { - SpirvValueKind::Def(id) | SpirvValueKind::IllegalConst(id) => { + SpirvValueKind::Def(id) => { let &entry = cx.builder.id_to_const.borrow().get(&id)?; match entry.val { SpirvConst::PtrTo { pointee } => { @@ -104,15 +99,9 @@ impl SpirvValue { SpirvType::Pointer { pointee } => pointee, ty => bug!("load called on value that wasn't a pointer: {:?}", ty), }; - // FIXME(eddyb) deduplicate this `if`-`else` and its other copies. - let kind = if entry.legal.is_ok() { - SpirvValueKind::Def(pointee) - } else { - SpirvValueKind::IllegalConst(pointee) - }; Some(SpirvValue { zombie_waiting_for_span: entry.legal.is_err(), - kind, + kind: SpirvValueKind::Def(pointee), ty, }) } @@ -152,7 +141,6 @@ impl SpirvValue { } SpirvValueKind::Def(id) - | SpirvValueKind::IllegalConst(id) | SpirvValueKind::LogicalPtrCast { original_ptr: _, original_ptr_ty: _, @@ -573,15 +561,9 @@ impl<'tcx> BuilderSpirv<'tcx> { let val_with_type = WithType { ty, val }; if let Some(entry) = self.const_to_id.borrow().get(&val_with_type) { - // FIXME(eddyb) deduplicate this `if`-`else` and its other copies. - let kind = if entry.legal.is_ok() { - SpirvValueKind::Def(entry.val) - } else { - SpirvValueKind::IllegalConst(entry.val) - }; return SpirvValue { zombie_waiting_for_span: entry.legal.is_err(), - kind, + kind: SpirvValueKind::Def(entry.val), ty, }; } @@ -784,15 +766,9 @@ impl<'tcx> BuilderSpirv<'tcx> { .insert(id, WithConstLegality { val, legal }), None ); - // FIXME(eddyb) deduplicate this `if`-`else` and its other copies. - let kind = if legal.is_ok() { - SpirvValueKind::Def(id) - } else { - SpirvValueKind::IllegalConst(id) - }; SpirvValue { zombie_waiting_for_span: legal.is_err(), - kind, + kind: SpirvValueKind::Def(id), ty, } } @@ -803,9 +779,7 @@ impl<'tcx> BuilderSpirv<'tcx> { pub fn lookup_const(&self, def: SpirvValue) -> Option> { match def.kind { - SpirvValueKind::Def(id) | SpirvValueKind::IllegalConst(id) => { - self.lookup_const_by_id(id) - } + SpirvValueKind::Def(id) => self.lookup_const_by_id(id), _ => None, } } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs index eb8783049c..1eef73b3f1 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs @@ -3,7 +3,7 @@ use crate::maybe_pqp_cg_ssa as rustc_codegen_ssa; use super::CodegenCx; use crate::abi::ConvSpirvType; -use crate::builder_spirv::{SpirvConst, SpirvValue, SpirvValueExt, SpirvValueKind}; +use crate::builder_spirv::{SpirvConst, SpirvValue, SpirvValueExt}; use crate::spirv_type::SpirvType; use itertools::Itertools as _; use rspirv::spirv::Word; @@ -334,8 +334,7 @@ impl<'tcx> CodegenCx<'tcx> { pub fn const_bitcast(&self, val: SpirvValue, ty: Word) -> SpirvValue { // HACK(eddyb) special-case `const_data_from_alloc` + `static_addr_of` // as the old `from_const_alloc` (now `OperandRef::from_const_alloc`). - if let SpirvValueKind::IllegalConst(_) = val.kind - && let Some(SpirvConst::PtrTo { pointee }) = self.builder.lookup_const(val) + if let Some(SpirvConst::PtrTo { pointee }) = self.builder.lookup_const(val) && let Some(SpirvConst::ConstDataFromAlloc(alloc)) = self.builder.lookup_const_by_id(pointee) && let SpirvType::Pointer { pointee } = self.lookup_type(ty) From ccb2e397ecddcb1d80e687b9692333a3a269c255 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 24 Jul 2025 19:52:39 +0300 Subject: [PATCH 5/5] Reduce `SpirvValue` lossiness around `strip_ptrcasts` and `const_fold_load`. --- .../src/builder/builder_methods.rs | 31 ++- .../rustc_codegen_spirv/src/builder_spirv.rs | 194 +++++++++--------- .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 5 +- 3 files changed, 120 insertions(+), 110 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 30c7d7f391..389e9a763d 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -2453,8 +2453,8 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { } // Defer the cast so that it has a chance to be avoided. - let original_ptr = ptr.def(self); - let bitcast_result_id = self.emit().bitcast(dest_ty, None, original_ptr).unwrap(); + let ptr_id = ptr.def(self); + let bitcast_result_id = self.emit().bitcast(dest_ty, None, ptr_id).unwrap(); self.zombie( bitcast_result_id, @@ -2469,10 +2469,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { SpirvValue { zombie_waiting_for_span: false, - kind: SpirvValueKind::LogicalPtrCast { - original_ptr, - original_ptr_ty: ptr.ty, - bitcast_result_id, + kind: SpirvValueKind::Def { + id: bitcast_result_id, + original_ptr_before_casts: Some(SpirvValue { + zombie_waiting_for_span: ptr.zombie_waiting_for_span, + kind: ptr_id, + ty: ptr.ty, + }), }, ty: dest_ty, } @@ -3289,7 +3292,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { return_type, arguments, } => ( - if let SpirvValueKind::FnAddr { function } = callee.kind { + if let SpirvValueKind::FnAddr { function, .. } = callee.kind { assert_ty_eq!(self, callee_ty, pointee); function } @@ -3426,11 +3429,11 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { // HACK(eddyb) some entry-points only take a `&str`, not `fmt::Arguments`. if let [ SpirvValue { - kind: SpirvValueKind::Def(a_id), + kind: SpirvValueKind::Def { id: a_id, .. }, .. }, SpirvValue { - kind: SpirvValueKind::Def(b_id), + kind: SpirvValueKind::Def { id: b_id, .. }, .. }, ref other_args @ .., @@ -3449,14 +3452,20 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { // HACK(eddyb) `panic_nounwind_fmt` takes an extra argument. [ SpirvValue { - kind: SpirvValueKind::Def(format_args_id), + kind: + SpirvValueKind::Def { + id: format_args_id, .. + }, .. }, _, // `&'static panic::Location<'static>` ] | [ SpirvValue { - kind: SpirvValueKind::Def(format_args_id), + kind: + SpirvValueKind::Def { + id: format_args_id, .. + }, .. }, _, // `force_no_backtrace: bool` diff --git a/crates/rustc_codegen_spirv/src/builder_spirv.rs b/crates/rustc_codegen_spirv/src/builder_spirv.rs index ecb97b792e..f430dfdf7f 100644 --- a/crates/rustc_codegen_spirv/src/builder_spirv.rs +++ b/crates/rustc_codegen_spirv/src/builder_spirv.rs @@ -16,7 +16,6 @@ use rustc_abi::Size; use rustc_arena::DroplessArena; use rustc_codegen_ssa::traits::ConstCodegenMethods as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_middle::bug; use rustc_middle::mir::interpret::ConstAllocation; use rustc_middle::ty::TyCtxt; use rustc_span::source_map::SourceMap; @@ -31,40 +30,37 @@ use std::str; use std::sync::Arc; use std::{fs::File, io::Write, path::Path}; +// HACK(eddyb) silence warnings that are inaccurate wrt future changes. +#[non_exhaustive] #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum SpirvValueKind { - Def(Word), + Def { + id: Word, + + /// If `id` is a pointer cast, this will be `Some`, and contain all the + /// information necessary to regenerate the original `SpirvValue` before + /// *any* pointer casts were applied, effectively deferring the casts + /// (as long as all downstream uses apply `.strip_ptrcasts()` first), + /// and bypassing errors they might cause (due to SPIR-V limitations). + // + // FIXME(eddyb) wouldn't it be easier to use this for *any* bitcasts? + // (with some caveats around dedicated int<->ptr casts vs bitcasts) + original_ptr_before_casts: Option>, + }, // FIXME(eddyb) this shouldn't be needed, but `rustc_codegen_ssa` still relies // on converting `Function`s to `Value`s even for direct calls, the `Builder` // should just have direct and indirect `call` variants (or a `Callee` enum). FnAddr { function: Word, - }, - - /// Deferred pointer cast, for the `Logical` addressing model (which doesn't - /// really support raw pointers in the way Rust expects to be able to use). - /// - /// The cast's target pointer type is the `ty` of the `SpirvValue` that has - /// `LogicalPtrCast` as its `kind`, as it would be redundant to have it here. - LogicalPtrCast { - /// Pointer value being cast. - original_ptr: Word, - /// Pointer type of `original_ptr`. - original_ptr_ty: Word, - - /// Result ID for the `OpBitcast` instruction representing the cast, - /// to attach zombies to. - // - // HACK(eddyb) having an `OpBitcast` only works by being DCE'd away, - // or by being replaced with a noop in `qptr::lower`. - bitcast_result_id: Word, + // FIXME(eddyb) replace this ad-hoc zombie with a proper `SpirvConst`. + zombie_id: Word, }, } #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct SpirvValue { +pub struct SpirvValue { // HACK(eddyb) used to cheaply check whether this is a SPIR-V value ID // with a "zombie" (deferred error) attached to it, that may need a `Span` // still (e.g. such as constants, which can't easily take a `Span`). @@ -72,43 +68,48 @@ pub struct SpirvValue { // which may make `SpirvValue` smaller requires far too much impl effort. pub zombie_waiting_for_span: bool, - pub kind: SpirvValueKind, + pub kind: K, pub ty: Word, } +impl SpirvValue { + fn map_kind(self, f: impl FnOnce(K) -> K2) -> SpirvValue { + let SpirvValue { + zombie_waiting_for_span, + kind, + ty, + } = self; + SpirvValue { + zombie_waiting_for_span, + kind: f(kind), + ty, + } + } +} + impl SpirvValue { pub fn strip_ptrcasts(self) -> Self { match self.kind { - SpirvValueKind::LogicalPtrCast { - original_ptr, - original_ptr_ty, - bitcast_result_id: _, - } => original_ptr.with_type(original_ptr_ty), + SpirvValueKind::Def { + id: _, + original_ptr_before_casts: Some(original_ptr), + } => original_ptr.map_kind(|id| SpirvValueKind::Def { + id, + original_ptr_before_casts: None, + }), _ => self, } } pub fn const_fold_load(self, cx: &CodegenCx<'_>) -> Option { - match self.kind { - SpirvValueKind::Def(id) => { - let &entry = cx.builder.id_to_const.borrow().get(&id)?; - match entry.val { - SpirvConst::PtrTo { pointee } => { - let ty = match cx.lookup_type(self.ty) { - SpirvType::Pointer { pointee } => pointee, - ty => bug!("load called on value that wasn't a pointer: {:?}", ty), - }; - Some(SpirvValue { - zombie_waiting_for_span: entry.legal.is_err(), - kind: SpirvValueKind::Def(pointee), - ty, - }) - } - _ => None, - } + match cx.builder.lookup_const(self)? { + SpirvConst::PtrTo { pointee } => { + // HACK(eddyb) this obtains a `SpirvValue` from the ID it contains, + // so there's some conceptual inefficiency there, but it does + // prevent any of the other details from being lost accidentally. + Some(cx.builder.id_to_const_and_val.borrow().get(&pointee)?.val.1) } - _ => None, } } @@ -128,24 +129,7 @@ impl SpirvValue { pub fn def_with_span(self, cx: &CodegenCx<'_>, span: Span) -> Word { let id = match self.kind { - SpirvValueKind::FnAddr { .. } => { - cx.builder - .const_to_id - .borrow() - .get(&WithType { - ty: self.ty, - val: SpirvConst::ZombieUndefForFnAddr, - }) - .expect("FnAddr didn't go through proper undef registration") - .val - } - - SpirvValueKind::Def(id) - | SpirvValueKind::LogicalPtrCast { - original_ptr: _, - original_ptr_ty: _, - bitcast_result_id: id, - } => id, + SpirvValueKind::Def { id, .. } | SpirvValueKind::FnAddr { zombie_id: id, .. } => id, }; if self.zombie_waiting_for_span { cx.add_span_to_zombie_if_missing(id, span); @@ -162,7 +146,10 @@ impl SpirvValueExt for Word { fn with_type(self, ty: Word) -> SpirvValue { SpirvValue { zombie_waiting_for_span: false, - kind: SpirvValueKind::Def(self), + kind: SpirvValueKind::Def { + id: self, + original_ptr_before_casts: None, + }, ty, } } @@ -380,11 +367,12 @@ pub struct BuilderSpirv<'tcx> { builder: RefCell, // Bidirectional maps between `SpirvConst` and the ID of the defined global - // (e.g. `OpConstant...`) instruction. - // NOTE(eddyb) both maps have `WithConstLegality` around their keys, which - // allows getting that legality information without additional lookups. - const_to_id: RefCell>, WithConstLegality>>, - id_to_const: RefCell>>>, + // (e.g. `OpConstant...`) instruction, with additional information in values + // (i.e. each map is keyed by only some part of the other map's value type), + // as needed to streamline operations (e.g. avoiding rederiving `SpirvValue`). + const_to_val: RefCell>, SpirvValue>>, + id_to_const_and_val: + RefCell, SpirvValue)>>>, debug_file_cache: RefCell>>, @@ -455,8 +443,8 @@ impl<'tcx> BuilderSpirv<'tcx> { source_map: tcx.sess.source_map(), dropless_arena: &tcx.arena.dropless, builder: RefCell::new(builder), - const_to_id: Default::default(), - id_to_const: Default::default(), + const_to_val: Default::default(), + id_to_const_and_val: Default::default(), debug_file_cache: Default::default(), enabled_capabilities, } @@ -560,12 +548,8 @@ impl<'tcx> BuilderSpirv<'tcx> { }; let val_with_type = WithType { ty, val }; - if let Some(entry) = self.const_to_id.borrow().get(&val_with_type) { - return SpirvValue { - zombie_waiting_for_span: entry.legal.is_err(), - kind: SpirvValueKind::Def(entry.val), - ty, - }; + if let Some(&v) = self.const_to_val.borrow().get(&val_with_type) { + return v; } let val = val_with_type.val; @@ -697,11 +681,11 @@ impl<'tcx> BuilderSpirv<'tcx> { SpirvConst::Composite(v) => v .iter() .map(|field| { - let field_entry = &self.id_to_const.borrow()[field]; + let field_entry = &self.id_to_const_and_val.borrow()[field]; field_entry.legal.and( // `field` is itself some legal `SpirvConst`, but can we have // it as part of an `OpConstantComposite`? - match field_entry.val { + match field_entry.val.0 { SpirvConst::PtrTo { .. } => Err(IllegalConst::Shallow( LeafIllegalConst::CompositeContainsPtrTo, )), @@ -729,14 +713,16 @@ impl<'tcx> BuilderSpirv<'tcx> { }) .unwrap_or(Ok(())), - SpirvConst::PtrTo { pointee } => match self.id_to_const.borrow()[&pointee].legal { - Ok(()) => Ok(()), + SpirvConst::PtrTo { pointee } => { + match self.id_to_const_and_val.borrow()[&pointee].legal { + Ok(()) => Ok(()), - // `Shallow` becomes `Indirect` when placed behind a pointer. - Err(IllegalConst::Shallow(cause) | IllegalConst::Indirect(cause)) => { - Err(IllegalConst::Indirect(cause)) + // `Shallow` becomes `Indirect` when placed behind a pointer. + Err(IllegalConst::Shallow(cause) | IllegalConst::Indirect(cause)) => { + Err(IllegalConst::Indirect(cause)) + } } - }, + } SpirvConst::ConstDataFromAlloc(_) => Err(IllegalConst::Shallow( LeafIllegalConst::UntypedConstDataFromAlloc, @@ -754,32 +740,44 @@ impl<'tcx> BuilderSpirv<'tcx> { } let val = val.tcx_arena_alloc_slices(cx); + + // FIXME(eddyb) the `val`/`v` name clash is a bit unfortunate. + let v = SpirvValue { + zombie_waiting_for_span: legal.is_err(), + kind: SpirvValueKind::Def { + id, + original_ptr_before_casts: None, + }, + ty, + }; + assert_matches!( - self.const_to_id + self.const_to_val .borrow_mut() - .insert(WithType { ty, val }, WithConstLegality { val: id, legal }), + .insert(WithType { ty, val }, v), None ); assert_matches!( - self.id_to_const - .borrow_mut() - .insert(id, WithConstLegality { val, legal }), + self.id_to_const_and_val.borrow_mut().insert( + id, + WithConstLegality { + val: (val, v), + legal + } + ), None ); - SpirvValue { - zombie_waiting_for_span: legal.is_err(), - kind: SpirvValueKind::Def(id), - ty, - } + + v } pub fn lookup_const_by_id(&self, id: Word) -> Option> { - Some(self.id_to_const.borrow().get(&id)?.val) + Some(self.id_to_const_and_val.borrow().get(&id)?.val.0) } pub fn lookup_const(&self, def: SpirvValue) -> Option> { match def.kind { - SpirvValueKind::Def(id) => self.lookup_const_by_id(id), + SpirvValueKind::Def { id, .. } => self.lookup_const_by_id(id), _ => None, } } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index ffec195a21..fcebbde3f3 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -854,12 +854,15 @@ impl<'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'tcx> { // Create these `OpUndef`s up front, instead of on-demand in `SpirvValue::def`, // because `SpirvValue::def` can't use `cx.emit()`. - self.def_constant(ty, SpirvConst::ZombieUndefForFnAddr); + let zombie_id = self + .def_constant(ty, SpirvConst::ZombieUndefForFnAddr) + .def_with_span(self, span); SpirvValue { zombie_waiting_for_span: false, kind: SpirvValueKind::FnAddr { function: function.id, + zombie_id, }, ty, }