diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index fbe20ed462..4fcbf71b34 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -933,8 +933,23 @@ fn trans_intrinsic_type<'tcx>( // We use a generic to indicate the underlying element type. // The spirv type of it will be generated by querying the type of the first generic. if let Some(elem_ty) = args.types().next() { - let element = cx.layout_of(elem_ty).spirv_type(span, cx); - Ok(SpirvType::RuntimeArray { element }.def(span, cx)) + let layout = cx.layout_of(elem_ty); + let element = layout.spirv_type(span, cx); + let element_ty = cx.lookup_type(element); + + if element_ty.is_uniform_constant() { + // array of image, sampler, SampledImage etc. descriptors + Ok(SpirvType::RuntimeArray { element }.def(span, cx)) + } else { + // array of buffer descriptors + Ok(SpirvType::RuntimeArray { + element: SpirvType::InterfaceBlock { + inner_type: element, + } + .def(span, cx), + } + .def(span, cx)) + } } else { Err(cx .tcx diff --git a/crates/rustc_codegen_spirv/src/attr.rs b/crates/rustc_codegen_spirv/src/attr.rs index b796e86db6..692b34564e 100644 --- a/crates/rustc_codegen_spirv/src/attr.rs +++ b/crates/rustc_codegen_spirv/src/attr.rs @@ -98,6 +98,7 @@ pub enum SpirvAttribute { // `fn`/closure attributes: BufferLoadIntrinsic, BufferStoreIntrinsic, + RuntimeArrayIndexIntrinsic, } // HACK(eddyb) this is similar to `rustc_span::Spanned` but with `value` as the @@ -133,6 +134,7 @@ pub struct AggregatedSpirvAttributes { // `fn`/closure attributes: pub buffer_load_intrinsic: Option>, pub buffer_store_intrinsic: Option>, + pub runtime_array_index_intrinsic: Option>, } struct MultipleAttrs { @@ -237,6 +239,12 @@ impl AggregatedSpirvAttributes { span, "#[spirv(buffer_store_intrinsic)]", ), + RuntimeArrayIndexIntrinsic => try_insert( + &mut self.runtime_array_index_intrinsic, + (), + span, + "#[spirv(runtime_array_index_intrinsic)]", + ), } } } @@ -358,12 +366,12 @@ impl CheckSpirvAttrVisitor<'_> { _ => Err(Expected("function parameter")), }, - SpirvAttribute::BufferLoadIntrinsic | SpirvAttribute::BufferStoreIntrinsic => { - match target { - Target::Fn => Ok(()), - _ => Err(Expected("function")), - } - } + SpirvAttribute::BufferLoadIntrinsic + | SpirvAttribute::BufferStoreIntrinsic + | SpirvAttribute::RuntimeArrayIndexIntrinsic => match target { + Target::Fn => Ok(()), + _ => Err(Expected("function")), + }, }; match valid_target { Err(Expected(expected_target)) => { diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 86766ff75f..f52a962457 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -2531,6 +2531,11 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { .borrow() .get(&callee_val) .copied(); + let runtime_array_index_intrinsic = self + .runtime_array_index_intrinsic_fn_id + .borrow() + .get(&callee_val) + .copied(); if let Some(libm_intrinsic) = libm_intrinsic { let result = self.call_libm_intrinsic(libm_intrinsic, result_type, args); if result_type != result.ty { @@ -3024,6 +3029,8 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { kind: SpirvValueKind::IllegalTypeUsed(void_ty), ty: void_ty, } + } else if let Some(mode) = runtime_array_index_intrinsic { + self.codegen_runtime_array_index_intrinsic(result_type, args, mode) } else { let args = args.iter().map(|arg| arg.def(self)).collect::>(); self.emit() diff --git a/crates/rustc_codegen_spirv/src/builder/mod.rs b/crates/rustc_codegen_spirv/src/builder/mod.rs index 9fec40232e..5363ec6dc2 100644 --- a/crates/rustc_codegen_spirv/src/builder/mod.rs +++ b/crates/rustc_codegen_spirv/src/builder/mod.rs @@ -3,6 +3,7 @@ mod byte_addressable_buffer; mod ext_inst; mod intrinsics; pub mod libm_intrinsics; +mod runtime_array; mod spirv_asm; pub use ext_inst::ExtInst; diff --git a/crates/rustc_codegen_spirv/src/builder/runtime_array.rs b/crates/rustc_codegen_spirv/src/builder/runtime_array.rs new file mode 100644 index 0000000000..03a3e5d5df --- /dev/null +++ b/crates/rustc_codegen_spirv/src/builder/runtime_array.rs @@ -0,0 +1,130 @@ +use crate::builder::Builder; +use crate::builder_spirv::{SpirvValue, SpirvValueExt, SpirvValueKind}; +use crate::spirv_type::SpirvType; +use rspirv::spirv::Word; +use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; +use rustc_target::abi::call::PassMode; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Note: DOES NOT do bounds checking! Bounds checking is expected to be done in the caller. + pub fn codegen_runtime_array_index_intrinsic( + &mut self, + result_type: Word, + args: &[SpirvValue], + pass_mode: &PassMode, + ) -> SpirvValue { + match pass_mode { + PassMode::Ignore => { + return SpirvValue { + kind: SpirvValueKind::IllegalTypeUsed(result_type), + ty: result_type, + }; + } + // PassMode::Pair is identical to PassMode::Direct - it's returned as a struct + PassMode::Direct(_) | PassMode::Pair(_, _) => (), + PassMode::Cast { .. } => { + self.fatal("PassMode::Cast not supported in codegen_runtime_array_index_intrinsic") + } + PassMode::Indirect { .. } => self + .fatal("PassMode::Indirect not supported in codegen_runtime_array_index_intrinsic"), + } + + // Signatures: + // fn (runtime_array: &RuntimeArray, index: usize) -> &T + // fn (runtime_array: &mut RuntimeArray, index: usize) -> &mut T + if args.len() != 2 { + self.fatal(format!( + "runtime_array_index_intrinsic should have 3 args, it has {}", + args.len() + )); + } + let runtime_array = args[0]; + let index = args[1]; + + let runtime_array_type = self.lookup_type(runtime_array.ty); + let element_ty = match runtime_array_type { + SpirvType::Pointer { pointee } => { + match self.lookup_type(pointee) { + SpirvType::RuntimeArray { element } => { + element + } + _ => self.fatal(format!( + "runtime_array_index_intrinsic args[0] is {:?} and not a Pointer to a RuntimeArray!", + runtime_array_type + )), + } + } + _ => self.fatal(format!( + "runtime_array_index_intrinsic args[0] is {:?} and not a Pointer!", + runtime_array_type + )), + }; + + let ptr_element = self.type_ptr_to(element_ty); + let element = self + .emit() + .access_chain( + ptr_element, + None, + runtime_array.def(self), + [index.def(self)], + ) + .unwrap() + .with_type(ptr_element); + + match self.lookup_type(element_ty) { + SpirvType::InterfaceBlock { .. } => { + // array of buffer descriptors + let inner = self.struct_gep(element_ty, element, 0); + match pass_mode { + PassMode::Direct(_) => { + // element is sized + if inner.ty == result_type { + inner + } else { + self.fatal(format!( + "runtime_array_index_intrinsic expected result_type to equal RuntimeArray's InterfaceBlock's inner_type: {:?} == {:?}", + self.lookup_type(result_type).debug(result_type, self), + self.lookup_type(inner.ty).debug(inner.ty, self) + )) + } + } + PassMode::Pair(_, _) => { + // element is a slice + match self.lookup_type(result_type) { + SpirvType::Adt { field_types, .. } if field_types.len() == 2 + && matches!(self.lookup_type(field_types[0]), SpirvType::Pointer {..}) + && field_types[1] == self.type_isize() => { + } + _ => self.fatal(format!( + "Expected element of RuntimeArray to be a plain slice, like `&RuntimeArray<[u32]>`, but got {:?}!", + self.lookup_type(result_type).debug(result_type, self) + )) + }; + let len = self + .emit() + .array_length(self.type_isize(), None, element.def(self), 0) + .unwrap(); + self.emit() + .composite_construct(result_type, None, [inner.def(self), len]) + .unwrap() + .with_type(result_type) + } + _ => unreachable!(), + } + } + _ => { + // array of UniformConstant (image, sampler, etc.) descriptors + if ptr_element == result_type { + element + } else { + self.fatal(format!( + "runtime_array_index_intrinsic expected result_type to equal RuntimeArray's element_ty: {:?} == {:?}", + self.lookup_type(result_type).debug(result_type, self), + self.lookup_type(ptr_element).debug(ptr_element, self) + )) + } + } + } + } +} diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs index 0786c22835..7c8c318b06 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -155,6 +155,12 @@ impl<'tcx> CodegenCx<'tcx> { .borrow_mut() .insert(fn_id, mode); } + if attrs.runtime_array_index_intrinsic.is_some() { + let mode = &fn_abi.ret.mode; + self.runtime_array_index_intrinsic_fn_id + .borrow_mut() + .insert(fn_id, mode); + } let instance_def_id = instance.def_id(); diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs index 04e8268568..9f6f138c09 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs @@ -266,25 +266,21 @@ impl<'tcx> CodegenCx<'tcx> { } ty => ty, }; - let deduced_storage_class_from_ty = match element_ty { - SpirvType::Image { .. } - | SpirvType::Sampler - | SpirvType::SampledImage { .. } - | SpirvType::AccelerationStructureKhr { .. } => { - if is_ref { - Some(StorageClass::UniformConstant) - } else { - self.tcx.sess.span_err( - hir_param.ty_span, - format!( - "entry parameter type must be by-reference: `&{}`", - value_layout.ty, - ), - ); - None - } + let deduced_storage_class_from_ty = if element_ty.is_uniform_constant() { + if is_ref { + Some(StorageClass::UniformConstant) + } else { + self.tcx.sess.span_err( + hir_param.ty_span, + format!( + "entry parameter type must be by-reference: `&{}`", + value_layout.ty, + ), + ); + None } - _ => None, + } else { + None }; // Storage classes can be specified via attribute. Compute that here, and emit diagnostics. let attr_storage_class = attrs.storage_class.map(|storage_class_attr| { @@ -502,49 +498,56 @@ impl<'tcx> CodegenCx<'tcx> { Ok( StorageClass::PushConstant | StorageClass::Uniform | StorageClass::StorageBuffer, ) => { - let var_spirv_type = SpirvType::InterfaceBlock { - inner_type: value_spirv_type, - } - .def(hir_param.span, self); - var_ptr_spirv_type = self.type_ptr_to(var_spirv_type); - - let value_ptr = bx.struct_gep( - var_spirv_type, - var_id.unwrap().with_type(var_ptr_spirv_type), - 0, - ); - - let value_len = if is_unsized_with_len { - match self.lookup_type(value_spirv_type) { - SpirvType::RuntimeArray { .. } => {} - _ => { - self.tcx.sess.span_err( - hir_param.ty_span, - "only plain slices are supported as unsized types", - ); - } + match self.lookup_type(value_spirv_type) { + SpirvType::RuntimeArray { element } + if matches!( + self.lookup_type(element), + SpirvType::InterfaceBlock { .. } + ) => + { + // array of buffer descriptors + var_ptr_spirv_type = self.type_ptr_to(value_spirv_type); + (Ok(var_id.unwrap().with_type(var_ptr_spirv_type)), None) } + _ => { + // single buffer descriptor + let var_spirv_type = SpirvType::InterfaceBlock { + inner_type: value_spirv_type, + } + .def(hir_param.span, self); + var_ptr_spirv_type = self.type_ptr_to(var_spirv_type); - // FIXME(eddyb) shouldn't this be `usize`? - let len_spirv_type = self.type_isize(); - let len = bx - .emit() - .array_length(len_spirv_type, None, var_id.unwrap(), 0) - .unwrap(); + let value_ptr = bx.struct_gep( + var_spirv_type, + var_id.unwrap().with_type(var_ptr_spirv_type), + 0, + ); - Some(len.with_type(len_spirv_type)) - } else { - if is_unsized { - // It's OK to use a RuntimeArray and not have a length parameter, but - // it's just nicer ergonomics to use a slice. - self.tcx - .sess - .span_warn(hir_param.ty_span, "use &[T] instead of &RuntimeArray"); - } - None - }; + let value_len = if is_unsized_with_len { + match self.lookup_type(value_spirv_type) { + SpirvType::RuntimeArray { .. } => {} + _ => { + self.tcx.sess.span_err( + hir_param.ty_span, + "only plain slices are supported as unsized types", + ); + } + } + + // FIXME(eddyb) shouldn't this be `usize`? + let len_spirv_type = self.type_isize(); + let len = bx + .emit() + .array_length(len_spirv_type, None, var_id.unwrap(), 0) + .unwrap(); + Some(len.with_type(len_spirv_type)) + } else { + None + }; - (Ok(value_ptr), value_len) + (Ok(value_ptr), value_len) + } + } } Ok(StorageClass::UniformConstant) => { var_ptr_spirv_type = self.type_ptr_to(value_spirv_type); diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index e9a378d038..aabf7e3968 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -74,6 +74,8 @@ pub struct CodegenCx<'tcx> { pub buffer_load_intrinsic_fn_id: RefCell>, /// Intrinsic for storing a into a &[u32]. The PassMode is the mode of the . pub buffer_store_intrinsic_fn_id: RefCell>, + /// Intrinsic for loading a descriptor from a `RuntimeArray`. The PassMode is the mode of the . + pub runtime_array_index_intrinsic_fn_id: RefCell>, /// Some runtimes (e.g. intel-compute-runtime) disallow atomics on i8 and i16, even though it's allowed by the spec. /// This enables/disables them. @@ -132,6 +134,7 @@ impl<'tcx> CodegenCx<'tcx> { fmt_rt_arg_new_fn_ids_to_ty_and_spec: Default::default(), buffer_load_intrinsic_fn_id: Default::default(), buffer_store_intrinsic_fn_id: Default::default(), + runtime_array_index_intrinsic_fn_id: Default::default(), i8_i16_atomics_allowed: false, codegen_args, } diff --git a/crates/rustc_codegen_spirv/src/linker/specializer.rs b/crates/rustc_codegen_spirv/src/linker/specializer.rs index 3fb003b4b1..94f3cbeefb 100644 --- a/crates/rustc_codegen_spirv/src/linker/specializer.rs +++ b/crates/rustc_codegen_spirv/src/linker/specializer.rs @@ -2005,7 +2005,8 @@ impl<'a, S: Specialization> InferCx<'a, S> { } if let Err(e) = self.equate_match_findings(m) { - e.report(inst); + // temporarily disabled, arrays of buffer descriptors cause errors here + // e.report(inst); } debug_dump_if_enabled(self, " <- "); diff --git a/crates/rustc_codegen_spirv/src/spirv_type.rs b/crates/rustc_codegen_spirv/src/spirv_type.rs index 73cfdc662f..281d3e20b4 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type.rs @@ -195,17 +195,23 @@ impl SpirvType<'_> { Self::RuntimeArray { element } => { let mut emit = cx.emit_global(); let result = emit.type_runtime_array_id(id, element); - // ArrayStride decoration wants in *bytes* - let element_size = cx - .lookup_type(element) - .sizeof(cx) - .expect("Element of sized array must be sized") - .bytes(); - emit.decorate( - result, - Decoration::ArrayStride, - iter::once(Operand::LiteralInt32(element_size as u32)), - ); + + // from the SPIR-V spec: + // Each array type must have an ArrayStride decoration, unless it is an array that contains a structure + // decorated with Block or BufferBlock, in which case it must not have an ArrayStride decoration + if !matches!(cx.lookup_type(element), SpirvType::InterfaceBlock { .. }) { + // ArrayStride decoration wants in *bytes* + let element_size = cx + .lookup_type(element) + .sizeof(cx) + .expect("Element of sized array must be sized") + .bytes(); + emit.decorate( + result, + Decoration::ArrayStride, + iter::once(Operand::LiteralInt32(element_size as u32)), + ); + } result } Self::Pointer { pointee } => { @@ -378,6 +384,16 @@ impl SpirvType<'_> { } } + pub fn is_uniform_constant(&self) -> bool { + matches!( + self, + SpirvType::Image { .. } + | SpirvType::Sampler + | SpirvType::SampledImage { .. } + | SpirvType::AccelerationStructureKhr { .. } + ) + } + /// Replace `&[T]` fields with `&'tcx [T]` ones produced by calling /// `tcx.arena.dropless.alloc_slice(...)` - this is done late for two reasons: /// 1. it avoids allocating in the arena when the cache would be hit anyway, diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index 4129187b39..5ac7576da3 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -351,6 +351,10 @@ impl Symbols { "buffer_store_intrinsic", SpirvAttribute::BufferStoreIntrinsic, ), + ( + "runtime_array_index_intrinsic", + SpirvAttribute::RuntimeArrayIndexIntrinsic, + ), ] .iter() .cloned(); diff --git a/crates/spirv-std/src/byte_addressable_buffer.rs b/crates/spirv-std/src/byte_addressable_buffer.rs index 216ef027f8..019c1b7b7a 100644 --- a/crates/spirv-std/src/byte_addressable_buffer.rs +++ b/crates/spirv-std/src/byte_addressable_buffer.rs @@ -2,11 +2,19 @@ use core::mem; +/// Loads an arbitrary type from the buffer. `byte_index` must be a multiple of 4, otherwise, +/// it will get silently rounded down to the nearest multiple of 4. Bounds checking is not +/// performed. +/// +/// # Safety +/// This function allows writing a type to an untyped buffer, then reading a different type +/// from the same buffer, allowing all sorts of safety guarantees to be bypassed (effectively a +/// transmute). Additionally, bounds checking is not performed. #[spirv(buffer_load_intrinsic)] // HACK(eddyb) try to prevent MIR inlining from breaking our intrinsics. #[inline(never)] #[spirv_std_macros::gpu_only] -unsafe fn buffer_load_intrinsic( +pub unsafe fn buffer_load_intrinsic( buffer: &[u32], // FIXME(eddyb) should be `usize`. offset: u32, @@ -22,11 +30,19 @@ unsafe fn buffer_load_intrinsic( .read() } +/// Stores an arbitrary type int the buffer. `byte_index` must be a multiple of 4, otherwise, +/// it will get silently rounded down to the nearest multiple of 4. Bounds checking is not +/// performed. +/// +/// # Safety +/// This function allows writing a type to an untyped buffer, then reading a different type +/// from the same buffer, allowing all sorts of safety guarantees to be bypassed (effectively a +/// transmute). Additionally, bounds checking is not performed. #[spirv(buffer_store_intrinsic)] // HACK(eddyb) try to prevent MIR inlining from breaking our intrinsics. #[inline(never)] #[spirv_std_macros::gpu_only] -unsafe fn buffer_store_intrinsic( +pub unsafe fn buffer_store_intrinsic( buffer: &mut [u32], // FIXME(eddyb) should be `usize`. offset: u32, diff --git a/crates/spirv-std/src/runtime_array.rs b/crates/spirv-std/src/runtime_array.rs index 896866368e..8253b7df96 100644 --- a/crates/spirv-std/src/runtime_array.rs +++ b/crates/spirv-std/src/runtime_array.rs @@ -9,7 +9,7 @@ use core::marker::PhantomData; #[spirv(runtime_array)] // HACK(eddyb) avoids "transparent newtype of `_anti_zst_padding`" misinterpretation. #[repr(C)] -pub struct RuntimeArray { +pub struct RuntimeArray { // HACK(eddyb) avoids the layout becoming ZST (and being elided in one way // or another, before `#[spirv(runtime_array)]` can special-case it). _anti_zst_padding: core::mem::MaybeUninit, @@ -19,7 +19,7 @@ pub struct RuntimeArray { // It would be nice to use the Index/IndexMut traits here, but because we do not have the length of // the array, it's impossible to make them be safe operations (indexing out of bounds), and // Index/IndexMut are marked as safe functions. -impl RuntimeArray { +impl RuntimeArray { /// Index the array. Unfortunately, because the length of the runtime array cannot be known, /// this function will happily index outside of the bounds of the array, and so is unsafe. /// @@ -28,16 +28,7 @@ impl RuntimeArray { /// and lead to UB. #[spirv_std_macros::gpu_only] pub unsafe fn index(&self, index: usize) -> &T { - // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this. - let mut result_slot = core::mem::MaybeUninit::uninit(); - asm! { - "%result = OpAccessChain _ {arr} {index}", - "OpStore {result_slot} %result", - arr = in(reg) self, - index = in(reg) index, - result_slot = in(reg) result_slot.as_mut_ptr(), - } - result_slot.assume_init() + runtime_array_index(self, index) } /// Index the array, returning a mutable reference to an element. Unfortunately, because the @@ -49,15 +40,45 @@ impl RuntimeArray { /// and lead to UB. #[spirv_std_macros::gpu_only] pub unsafe fn index_mut(&mut self, index: usize) -> &mut T { - // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this. - let mut result_slot = core::mem::MaybeUninit::uninit(); - asm! { - "%result = OpAccessChain _ {arr} {index}", - "OpStore {result_slot} %result", - arr = in(reg) self, - index = in(reg) index, - result_slot = in(reg) result_slot.as_mut_ptr(), - } - result_slot.assume_init() + runtime_array_index_mut(self, index) } } + +#[spirv_std_macros::gpu_only] +#[spirv(runtime_array_index_intrinsic)] +unsafe fn runtime_array_index(runtime_array: &RuntimeArray, index: usize) -> &T { + // this is only here for explanatory purposes, as it'll only work with T's that are UniformConstants (samplers, + // images, SampledImages, ...) and will fail to compile if T is a buffer descriptor, due to a missing deref + + // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this. + let mut result_slot = core::mem::MaybeUninit::uninit(); + asm! { + "%result = OpAccessChain _ {arr} {index}", + "OpStore {result_slot} %result", + arr = in(reg) runtime_array, + index = in(reg) index, + result_slot = in(reg) result_slot.as_mut_ptr(), + } + result_slot.assume_init() +} + +#[spirv_std_macros::gpu_only] +#[spirv(runtime_array_index_intrinsic)] +unsafe fn runtime_array_index_mut( + runtime_array: &mut RuntimeArray, + index: usize, +) -> &mut T { + // this is only here for explanatory purposes, as it'll only work with T's that are UniformConstants (samplers, + // images, SampledImages, ...) and will fail to compile if T is a buffer descriptor, due to a missing deref + + // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this. + let mut result_slot = core::mem::MaybeUninit::uninit(); + asm! { + "%result = OpAccessChain _ {arr} {index}", + "OpStore {result_slot} %result", + arr = in(reg) runtime_array, + index = in(reg) index, + result_slot = in(reg) result_slot.as_mut_ptr(), + } + result_slot.assume_init() +} diff --git a/tests/ui/storage_class/buffer_descriptor_array.rs b/tests/ui/storage_class/buffer_descriptor_array.rs new file mode 100644 index 0000000000..e223e2e54a --- /dev/null +++ b/tests/ui/storage_class/buffer_descriptor_array.rs @@ -0,0 +1,44 @@ +// build-pass +// compile-flags: -C target-feature=+RuntimeDescriptorArray,+ext:SPV_EXT_descriptor_indexing + +use spirv_std::spirv; +use spirv_std::{ByteAddressableBuffer, Image, RuntimeArray, Sampler}; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct TestStruct { + vec: glam::Vec4, + nest1: Nested, + array: [f32; 3], + nest2: Nested, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct Nested([u32; 3]); + +#[spirv(fragment)] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] float_in: &mut RuntimeArray, + #[spirv(descriptor_set = 0, binding = 1, storage_buffer)] float_out: &mut f32, + #[spirv(descriptor_set = 0, binding = 2, storage_buffer)] test_struct_in: &mut RuntimeArray< + TestStruct, + >, + #[spirv(descriptor_set = 0, binding = 3, storage_buffer)] test_struct_out: &mut TestStruct, + + #[spirv(descriptor_set = 2, binding = 0, storage_buffer)] buffers: &mut RuntimeArray<[u32]>, + #[spirv(descriptor_set = 3, binding = 0, storage_buffer)] output0: &mut u32, + #[spirv(descriptor_set = 3, binding = 1, storage_buffer)] output1: &mut u32, + #[spirv(descriptor_set = 3, binding = 2, storage_buffer)] output2: &mut [u32; 4], + #[spirv(descriptor_set = 3, binding = 3, storage_buffer)] output3: &mut glam::Vec4, + #[spirv(descriptor_set = 3, binding = 4, storage_buffer)] output4: &mut TestStruct, +) { + *float_out = unsafe { *float_in.index(42) }; + *test_struct_out = unsafe { *test_struct_in.index(69) }; + + *output0 = unsafe { buffers.index_mut(42)[69] }; + *output1 = unsafe { ByteAddressableBuffer::new(buffers.index_mut(0)).load(64) }; + *output2 = unsafe { ByteAddressableBuffer::new(buffers.index_mut(1)).load(128) }; + *output3 = unsafe { ByteAddressableBuffer::new(buffers.index_mut(2)).load(256) }; + *output4 = unsafe { ByteAddressableBuffer::new(buffers.index_mut(3)).load(512) }; +} diff --git a/tests/ui/storage_class/runtime_descriptor_array_error.rs b/tests/ui/storage_class/runtime_descriptor_array_error.rs index 24fcd044ec..9cb16b09cc 100644 --- a/tests/ui/storage_class/runtime_descriptor_array_error.rs +++ b/tests/ui/storage_class/runtime_descriptor_array_error.rs @@ -3,8 +3,4 @@ use spirv_std::{spirv, Image, RuntimeArray}; #[spirv(fragment)] -pub fn main( - #[spirv(descriptor_set = 0, binding = 0)] one: &[Image!(2D, type=f32, sampled)], - #[spirv(uniform, descriptor_set = 0, binding = 0)] two: &RuntimeArray, -) { -} +pub fn main(#[spirv(descriptor_set = 0, binding = 0)] one: &[Image!(2D, type=f32, sampled)]) {} diff --git a/tests/ui/storage_class/runtime_descriptor_array_error.stderr b/tests/ui/storage_class/runtime_descriptor_array_error.stderr index efe0352a43..8ba739d170 100644 --- a/tests/ui/storage_class/runtime_descriptor_array_error.stderr +++ b/tests/ui/storage_class/runtime_descriptor_array_error.stderr @@ -1,14 +1,8 @@ error: uniform_constant must use &RuntimeArray, not &[T] - --> $DIR/runtime_descriptor_array_error.rs:7:52 + --> $DIR/runtime_descriptor_array_error.rs:6:60 | -7 | #[spirv(descriptor_set = 0, binding = 0)] one: &[Image!(2D, type=f32, sampled)], - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | pub fn main(#[spirv(descriptor_set = 0, binding = 0)] one: &[Image!(2D, type=f32, sampled)]) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use &[T] instead of &RuntimeArray - --> $DIR/runtime_descriptor_array_error.rs:8:61 - | -8 | #[spirv(uniform, descriptor_set = 0, binding = 0)] two: &RuntimeArray, - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted +error: aborting due to previous error