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