spirv_std/runtime_array.rs
1#[cfg(target_arch = "spirv")]
2use core::arch::asm;
3use core::marker::PhantomData;
4
5/// Dynamically-sized arrays in Rust carry around their length as the second half of a tuple.
6/// Unfortunately, sometimes SPIR-V provides an unsized array with no way of obtaining its length.
7/// Hence, this type represents something very similar to a slice, but with no way of knowing its
8/// length.
9#[spirv(runtime_array)]
10// HACK(eddyb) avoids "transparent newtype of `_anti_zst_padding`" misinterpretation.
11#[repr(C)]
12pub struct RuntimeArray<T> {
13 // HACK(eddyb) avoids the layout becoming ZST (and being elided in one way
14 // or another, before `#[spirv(runtime_array)]` can special-case it).
15 _anti_zst_padding: core::mem::MaybeUninit<u32>,
16 _phantom: PhantomData<T>,
17}
18
19// It would be nice to use the Index/IndexMut traits here, but because we do not have the length of
20// the array, it's impossible to make them be safe operations (indexing out of bounds), and
21// Index/IndexMut are marked as safe functions.
22impl<T> RuntimeArray<T> {
23 /// Index the array. Unfortunately, because the length of the runtime array cannot be known,
24 /// this function will happily index outside of the bounds of the array, and so is unsafe.
25 ///
26 /// # Safety
27 /// Bounds checking is not performed, and indexing outside the bounds of the array can happen,
28 /// and lead to UB.
29 #[spirv_std_macros::gpu_only]
30 pub unsafe fn index(&self, index: usize) -> &T {
31 // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this.
32 let mut result_slot = core::mem::MaybeUninit::uninit();
33 asm! {
34 "%result = OpAccessChain _ {arr} {index}",
35 "OpStore {result_slot} %result",
36 arr = in(reg) self,
37 index = in(reg) index,
38 result_slot = in(reg) result_slot.as_mut_ptr(),
39 }
40 result_slot.assume_init()
41 }
42
43 /// Index the array, returning a mutable reference to an element. Unfortunately, because the
44 /// length of the runtime array cannot be known, this function will happily index outside of
45 /// the bounds of the array, and so is unsafe.
46 ///
47 /// # Safety
48 /// Bounds checking is not performed, and indexing outside the bounds of the array can happen,
49 /// and lead to UB.
50 #[spirv_std_macros::gpu_only]
51 pub unsafe fn index_mut(&mut self, index: usize) -> &mut T {
52 // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this.
53 let mut result_slot = core::mem::MaybeUninit::uninit();
54 asm! {
55 "%result = OpAccessChain _ {arr} {index}",
56 "OpStore {result_slot} %result",
57 arr = in(reg) self,
58 index = in(reg) index,
59 result_slot = in(reg) result_slot.as_mut_ptr(),
60 }
61 result_slot.assume_init()
62 }
63}