• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::api::icd::{ArcedCLObject, CLResult};
2 use crate::api::types::*;
3 use crate::core::event::*;
4 use crate::core::queue::*;
5 
6 use mesa_rust_util::properties::Properties;
7 use rusticl_opencl_gen::*;
8 
9 use std::convert::TryInto;
10 use std::ffi::{c_void, CStr};
11 use std::iter::zip;
12 use std::mem::MaybeUninit;
13 use std::ops::BitAnd;
14 use std::sync::Arc;
15 use std::{cmp, mem};
16 
17 // TODO: use MaybeUninit::copy_from_slice once stable
maybe_uninit_copy_from_slice<T>(this: &mut [MaybeUninit<T>], src: &[T]) where T: Copy,18 pub fn maybe_uninit_copy_from_slice<T>(this: &mut [MaybeUninit<T>], src: &[T])
19 where
20     T: Copy,
21 {
22     // Assert this so we stick as close as possible to MaybeUninit::copy_from_slices behavior.
23     debug_assert_eq!(this.len(), src.len());
24 
25     for (dest, val) in zip(this, src) {
26         *dest = MaybeUninit::new(*val);
27     }
28 }
29 
30 // TODO: use MaybeUninit::slice_assume_init_ref once stable
slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T]31 pub const unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
32     // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that
33     // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`.
34     // The pointer obtained is valid since it refers to memory owned by `slice` which is a
35     // reference and thus guaranteed to be valid for reads.
36     unsafe { &*(slice as *const [_] as *const [T]) }
37 }
38 
39 /// Token to make sure that ClInfoValue::write is called
40 pub struct CLInfoRes {
41     _private: (),
42 }
43 
44 /// A helper class to simplify implementing so called "info" APIs in OpenCL, e.g. `clGetDeviceInfo`.
45 ///
46 /// Those APIs generally operate on opaque memory buffers and needs to be interpreted according to
47 /// the specific query being made. The generic parameter to the input and write functions should
48 /// always be explicitly specified.
49 pub struct CLInfoValue<'a> {
50     param_value: Option<&'a mut [MaybeUninit<u8>]>,
51     param_value_size_ret: Option<&'a mut MaybeUninit<usize>>,
52 }
53 
54 impl CLInfoValue<'_> {
55     /// # Safety
56     /// `param_value` and `param_value_size_ret` need to be valid memory allocations or null.
57     /// If `param_value` is not null it needs to point to an allocation of at least
58     /// `param_value_size` bytes.
new( param_value_size: usize, param_value: *mut c_void, param_value_size_ret: *mut usize, ) -> CLResult<Self>59     unsafe fn new(
60         param_value_size: usize,
61         param_value: *mut c_void,
62         param_value_size_ret: *mut usize,
63     ) -> CLResult<Self> {
64         let param_value_size_ret: *mut MaybeUninit<usize> = param_value_size_ret.cast();
65         let param_value = if !param_value.is_null() {
66             Some(unsafe { cl_slice::from_raw_parts_mut(param_value.cast(), param_value_size)? })
67         } else {
68             None
69         };
70 
71         Ok(Self {
72             param_value: param_value,
73             param_value_size_ret: unsafe { param_value_size_ret.as_mut() },
74         })
75     }
76 
finish() -> CLInfoRes77     fn finish() -> CLInfoRes {
78         CLInfoRes { _private: () }
79     }
80 
81     /// Used to read from the application provided data.
input<T>(&self) -> CLResult<&[MaybeUninit<T>]>82     pub fn input<T>(&self) -> CLResult<&[MaybeUninit<T>]> {
83         if let Some(param_value) = &self.param_value {
84             let count = param_value.len() / mem::size_of::<T>();
85             unsafe { cl_slice::from_raw_parts(param_value.as_ptr().cast(), count) }
86         } else {
87             Ok(&[])
88         }
89     }
90 
91     /// Writes the passed in value according to the generic. It is important to pass in the same
92     /// or compatible type as stated in the OpenCL specification.
93     ///
94     /// It also verifies that if a buffer was provided by the application it's big enough to hold
95     /// `t` and returns `Err(CL_INVALID_VALUE)` otherwise.
96     ///
97     /// Type specific details:
98     ///  - Compatible with C arrays are `T` (if only one element is to be returned), `Vec<T>` or `&[T]`
99     ///    types.
100     ///  - Compatible with C strings are all basic Rust string types.
101     ///  - `bool`s are automatically converted to `cl_bool`.
102     ///  - For queries which can return no data, `Option<T>` can be used.
103     ///  - For C property arrays (0-terminated arrays of `T`) the
104     ///    [mesa_rust_util::properties::Properties] type can be used.
105     ///
106     /// All types implementing [CLProp] are supported.
write<T: CLProp>(self, t: T) -> CLResult<CLInfoRes>107     pub fn write<T: CLProp>(self, t: T) -> CLResult<CLInfoRes> {
108         let count = t.count();
109         let bytes = count * mem::size_of::<T::Output>();
110 
111         // param_value is a pointer to memory where the appropriate result being queried is
112         // returned. If param_value is NULL, it is ignored.
113         if let Some(param_value) = self.param_value {
114             // CL_INVALID_VALUE [...] if size in bytes specified by param_value_size is < size of
115             // return type as specified in the Context Attributes table and param_value is not a
116             // NULL value.
117             if param_value.len() < bytes {
118                 return Err(CL_INVALID_VALUE);
119             }
120 
121             // SAFETY: Casting between types wrapped with MaybeUninit is fine, because it's up to
122             //         the caller to decide if it sound to read from it. Also the count passed in is
123             //         just the type adjusted size of the `param_value` slice.
124             let out =
125                 unsafe { cl_slice::from_raw_parts_mut(param_value.as_mut_ptr().cast(), count)? };
126 
127             t.write_to(out);
128         }
129 
130         // param_value_size_ret returns the actual size in bytes of data being queried by
131         // param_name. If param_value_size_ret is NULL, it is ignored.
132         if let Some(param_value_size_ret) = self.param_value_size_ret {
133             param_value_size_ret.write(bytes);
134         }
135 
136         Ok(Self::finish())
137     }
138 
139     /// Returns the size of the buffer to the application it needs to provide in order to
140     /// successfully execute the query.
141     ///
142     /// Some queries simply provide pointers where the implementation needs to write data to, e.g.
143     /// `CL_PROGRAM_BINARIES`. In that case it's meaningless to write back the same pointers. This
144     /// function can be used to skip those writes.
write_len_only<T: CLProp>(self, len: usize) -> CLResult<CLInfoRes>145     pub fn write_len_only<T: CLProp>(self, len: usize) -> CLResult<CLInfoRes> {
146         let bytes = len * mem::size_of::<T::Output>();
147 
148         // param_value_size_ret returns the actual size in bytes of data being queried by
149         // param_name. If param_value_size_ret is NULL, it is ignored.
150         if let Some(param_value_size_ret) = self.param_value_size_ret {
151             param_value_size_ret.write(bytes);
152         }
153 
154         Ok(Self::finish())
155     }
156 
157     /// Similar to `write` with the only difference that instead of a precomputed value, this can
158     /// take an iterator instead.
159     ///
160     /// This is useful when the data to be returned isn't cheap to compute or not already available.
161     ///
162     /// When the application only asks for the size of the result, the iterator isn't advanced at
163     /// all, only its length is used.
write_iter<T: CLProp<Output = T> + Copy>( self, iter: impl ExactSizeIterator<Item = T>, ) -> CLResult<CLInfoRes>164     pub fn write_iter<T: CLProp<Output = T> + Copy>(
165         self,
166         iter: impl ExactSizeIterator<Item = T>,
167     ) -> CLResult<CLInfoRes> {
168         let count = iter.len();
169         let bytes = count * mem::size_of::<T::Output>();
170 
171         // param_value is a pointer to memory where the appropriate result being queried is
172         // returned. If param_value is NULL, it is ignored.
173         if let Some(param_value) = self.param_value {
174             // CL_INVALID_VALUE [...] if size in bytes specified by param_value_size is < size of
175             // return type as specified in the Context Attributes table and param_value is not a
176             // NULL value.
177             if param_value.len() < bytes {
178                 return Err(CL_INVALID_VALUE);
179             }
180 
181             let out =
182                 unsafe { cl_slice::from_raw_parts_mut(param_value.as_mut_ptr().cast(), count)? };
183 
184             for (item, out) in zip(iter, out.iter_mut()) {
185                 *out = item;
186             }
187         }
188 
189         // param_value_size_ret returns the actual size in bytes of data being queried by
190         // param_name. If param_value_size_ret is NULL, it is ignored.
191         if let Some(param_value_size_ret) = self.param_value_size_ret {
192             param_value_size_ret.write(bytes);
193         }
194 
195         Ok(Self::finish())
196     }
197 }
198 
199 /// # Safety
200 ///
201 /// This trait helps implementing various OpenCL Query APIs, however care have to be taken that the
202 /// `query` implementation implements the corresponding query according to the OpenCL specification.
203 ///
204 /// Queries which don't have a size known at compile time are expected to be called twice:
205 ///  1. To ask the implementation of how much data will be returned. The application uses this
206 ///     to allocate enough memory to be passed into the next call.
207 ///  2. To actually execute the query.
208 ///
209 /// This trait abstracts this pattern properly away to make it easier to implement it.
210 ///
211 /// [CLInfoValue] contains helper functions to read and write data behind opaque buffers, the
212 /// applications using those OpenCL queries expect the implementation to behave accordingly.
213 ///
214 /// It is advised to explicitly specify the types of [CLInfoValue::input] and the various write
215 /// helpers.
216 pub unsafe trait CLInfo<I> {
query(&self, q: I, v: CLInfoValue) -> CLResult<CLInfoRes>217     fn query(&self, q: I, v: CLInfoValue) -> CLResult<CLInfoRes>;
218 
219     /// # Safety
220     ///
221     /// Same requirements from [CLInfoValue::new] apply.
get_info( &self, param_name: I, param_value_size: usize, param_value: *mut ::std::os::raw::c_void, param_value_size_ret: *mut usize, ) -> CLResult<()>222     unsafe fn get_info(
223         &self,
224         param_name: I,
225         param_value_size: usize,
226         param_value: *mut ::std::os::raw::c_void,
227         param_value_size_ret: *mut usize,
228     ) -> CLResult<()> {
229         // SAFETY: It's up to the caller as this function is marked unsafe.
230         let value =
231             unsafe { CLInfoValue::new(param_value_size, param_value, param_value_size_ret)? };
232         self.query(param_name, value)?;
233         Ok(())
234     }
235 }
236 
237 /// # Safety
238 ///
239 /// See [CLInfo]
240 pub unsafe trait CLInfoObj<I, O> {
query(&self, o: O, q: I, v: CLInfoValue) -> CLResult<CLInfoRes>241     fn query(&self, o: O, q: I, v: CLInfoValue) -> CLResult<CLInfoRes>;
242 
243     /// # Safety
244     ///
245     /// Same requirements from [CLInfoValue::new] apply.
get_info_obj( &self, obj: O, param_name: I, param_value_size: usize, param_value: *mut ::std::os::raw::c_void, param_value_size_ret: *mut usize, ) -> CLResult<()>246     unsafe fn get_info_obj(
247         &self,
248         obj: O,
249         param_name: I,
250         param_value_size: usize,
251         param_value: *mut ::std::os::raw::c_void,
252         param_value_size_ret: *mut usize,
253     ) -> CLResult<()> {
254         // SAFETY: It's up to the caller as this function is marked unsafe.
255         let value =
256             unsafe { CLInfoValue::new(param_value_size, param_value, param_value_size_ret)? };
257         self.query(obj, param_name, value)?;
258         Ok(())
259     }
260 }
261 
262 /// Trait to be implemented for the [CLInfo] and [CLInfoObj].
263 pub trait CLProp {
264     type Output: CLProp + Copy;
265 
266     /// Returns the amount of `Self::Output` returned.
count(&self) -> usize267     fn count(&self) -> usize;
268 
269     /// Called to write the value into the `out` buffer.
write_to(&self, out: &mut [MaybeUninit<Self::Output>])270     fn write_to(&self, out: &mut [MaybeUninit<Self::Output>]);
271 }
272 
273 macro_rules! cl_prop_for_type {
274     ($ty: ty) => {
275         impl CLProp for $ty {
276             type Output = Self;
277 
278             fn count(&self) -> usize {
279                 1
280             }
281 
282             fn write_to(&self, out: &mut [MaybeUninit<Self>]) {
283                 out[0].write(*self);
284             }
285         }
286     };
287 }
288 
289 cl_prop_for_type!(cl_char);
290 cl_prop_for_type!(cl_uchar);
291 cl_prop_for_type!(cl_ushort);
292 cl_prop_for_type!(cl_int);
293 cl_prop_for_type!(cl_uint);
294 cl_prop_for_type!(cl_ulong);
295 cl_prop_for_type!(isize);
296 cl_prop_for_type!(usize);
297 
298 cl_prop_for_type!(cl_device_integer_dot_product_acceleration_properties_khr);
299 cl_prop_for_type!(cl_device_pci_bus_info_khr);
300 cl_prop_for_type!(cl_image_format);
301 cl_prop_for_type!(cl_name_version);
302 
303 impl CLProp for bool {
304     type Output = cl_bool;
305 
count(&self) -> usize306     fn count(&self) -> usize {
307         1
308     }
309 
write_to(&self, out: &mut [MaybeUninit<cl_bool>])310     fn write_to(&self, out: &mut [MaybeUninit<cl_bool>]) {
311         if *self { CL_TRUE } else { CL_FALSE }.write_to(out);
312     }
313 }
314 
315 impl CLProp for &str {
316     type Output = u8;
317 
count(&self) -> usize318     fn count(&self) -> usize {
319         // we need one additional byte for the nul terminator
320         self.len() + 1
321     }
322 
write_to(&self, out: &mut [MaybeUninit<u8>])323     fn write_to(&self, out: &mut [MaybeUninit<u8>]) {
324         let bytes = self.as_bytes();
325 
326         maybe_uninit_copy_from_slice(&mut out[0..bytes.len()], bytes);
327         out[bytes.len()].write(b'\0');
328     }
329 }
330 
331 impl CLProp for &CStr {
332     type Output = u8;
333 
count(&self) -> usize334     fn count(&self) -> usize {
335         self.to_bytes_with_nul().len()
336     }
337 
write_to(&self, out: &mut [MaybeUninit<u8>])338     fn write_to(&self, out: &mut [MaybeUninit<u8>]) {
339         self.to_bytes_with_nul().write_to(out);
340     }
341 }
342 
343 impl<T> CLProp for Vec<T>
344 where
345     T: CLProp + Copy,
346 {
347     type Output = T;
348 
count(&self) -> usize349     fn count(&self) -> usize {
350         self.len()
351     }
352 
write_to(&self, out: &mut [MaybeUninit<T>])353     fn write_to(&self, out: &mut [MaybeUninit<T>]) {
354         self.as_slice().write_to(out);
355     }
356 }
357 
358 impl<T> CLProp for &[T]
359 where
360     T: CLProp + Copy,
361 {
362     type Output = T;
363 
count(&self) -> usize364     fn count(&self) -> usize {
365         self.len()
366     }
367 
write_to(&self, out: &mut [MaybeUninit<T>])368     fn write_to(&self, out: &mut [MaybeUninit<T>]) {
369         maybe_uninit_copy_from_slice(&mut out[0..self.len()], self);
370     }
371 }
372 
373 impl<T, const I: usize> CLProp for [T; I]
374 where
375     T: CLProp + Copy,
376 {
377     type Output = Self;
378 
count(&self) -> usize379     fn count(&self) -> usize {
380         1
381     }
382 
write_to(&self, out: &mut [MaybeUninit<Self>])383     fn write_to(&self, out: &mut [MaybeUninit<Self>]) {
384         out[0].write(*self);
385     }
386 }
387 
388 impl<T> CLProp for *mut T {
389     type Output = Self;
390 
count(&self) -> usize391     fn count(&self) -> usize {
392         1
393     }
394 
write_to(&self, out: &mut [MaybeUninit<Self>])395     fn write_to(&self, out: &mut [MaybeUninit<Self>]) {
396         out[0].write(*self);
397     }
398 }
399 
400 impl<T> CLProp for &Properties<T>
401 where
402     T: CLProp + Copy + Default,
403 {
404     type Output = T;
405 
count(&self) -> usize406     fn count(&self) -> usize {
407         self.raw_data().count()
408     }
409 
write_to(&self, out: &mut [MaybeUninit<T>])410     fn write_to(&self, out: &mut [MaybeUninit<T>]) {
411         self.raw_data().write_to(out);
412     }
413 }
414 
415 impl<T> CLProp for Option<T>
416 where
417     T: CLProp + Copy,
418 {
419     type Output = T::Output;
420 
count(&self) -> usize421     fn count(&self) -> usize {
422         match self {
423             Some(val) => val.count(),
424             None => 0,
425         }
426     }
427 
write_to(&self, out: &mut [MaybeUninit<T::Output>])428     fn write_to(&self, out: &mut [MaybeUninit<T::Output>]) {
429         if let Some(val) = self {
430             val.write_to(out);
431         };
432     }
433 }
434 
435 const CL_DEVICE_TYPES: u32 = CL_DEVICE_TYPE_ACCELERATOR
436     | CL_DEVICE_TYPE_CPU
437     | CL_DEVICE_TYPE_GPU
438     | CL_DEVICE_TYPE_CUSTOM
439     | CL_DEVICE_TYPE_DEFAULT;
440 
check_cl_device_type(val: cl_device_type) -> CLResult<()>441 pub fn check_cl_device_type(val: cl_device_type) -> CLResult<()> {
442     let v: u32 = val.try_into().or(Err(CL_INVALID_DEVICE_TYPE))?;
443     if v == CL_DEVICE_TYPE_ALL || v & CL_DEVICE_TYPES == v {
444         return Ok(());
445     }
446     Err(CL_INVALID_DEVICE_TYPE)
447 }
448 
449 pub const CL_IMAGE_TYPES: [cl_mem_object_type; 6] = [
450     CL_MEM_OBJECT_IMAGE1D,
451     CL_MEM_OBJECT_IMAGE2D,
452     CL_MEM_OBJECT_IMAGE3D,
453     CL_MEM_OBJECT_IMAGE1D_ARRAY,
454     CL_MEM_OBJECT_IMAGE2D_ARRAY,
455     CL_MEM_OBJECT_IMAGE1D_BUFFER,
456 ];
457 
check_cl_bool<T: PartialEq + TryInto<cl_uint>>(val: T) -> Option<bool>458 pub fn check_cl_bool<T: PartialEq + TryInto<cl_uint>>(val: T) -> Option<bool> {
459     let c: u32 = val.try_into().ok()?;
460     if c != CL_TRUE && c != CL_FALSE {
461         return None;
462     }
463     Some(c == CL_TRUE)
464 }
465 
event_list_from_cl( q: &Arc<Queue>, num_events_in_wait_list: cl_uint, event_wait_list: *const cl_event, ) -> CLResult<Vec<Arc<Event>>>466 pub fn event_list_from_cl(
467     q: &Arc<Queue>,
468     num_events_in_wait_list: cl_uint,
469     event_wait_list: *const cl_event,
470 ) -> CLResult<Vec<Arc<Event>>> {
471     // CL_INVALID_EVENT_WAIT_LIST if event_wait_list is NULL and num_events_in_wait_list > 0, or
472     // event_wait_list is not NULL and num_events_in_wait_list is 0, or if event objects in
473     // event_wait_list are not valid events.
474     let res = Event::arcs_from_arr(event_wait_list, num_events_in_wait_list)
475         .map_err(|_| CL_INVALID_EVENT_WAIT_LIST)?;
476 
477     // CL_INVALID_CONTEXT if context associated with command_queue and events in event_list are not
478     // the same.
479     if res.iter().any(|e| e.context != q.context) {
480         return Err(CL_INVALID_CONTEXT);
481     }
482 
483     Ok(res)
484 }
485 
checked_compare(a: usize, o: cmp::Ordering, b: u64) -> bool486 pub fn checked_compare(a: usize, o: cmp::Ordering, b: u64) -> bool {
487     if usize::BITS > u64::BITS {
488         a.cmp(&(b as usize)) == o
489     } else {
490         (a as u64).cmp(&b) == o
491     }
492 }
493 
is_alligned<T>(ptr: *const T, alignment: usize) -> bool494 pub fn is_alligned<T>(ptr: *const T, alignment: usize) -> bool {
495     ptr as usize & (alignment - 1) == 0
496 }
497 
bit_check<A: BitAnd<Output = A> + PartialEq + Default, B: Into<A>>(a: A, b: B) -> bool498 pub fn bit_check<A: BitAnd<Output = A> + PartialEq + Default, B: Into<A>>(a: A, b: B) -> bool {
499     a & b.into() != A::default()
500 }
501 
502 // Taken from "Appendix D: Checking for Memory Copy Overlap"
503 // src_offset and dst_offset are additions to support sub-buffers
check_copy_overlap( src_origin: &CLVec<usize>, src_offset: usize, dst_origin: &CLVec<usize>, dst_offset: usize, region: &CLVec<usize>, row_pitch: usize, slice_pitch: usize, ) -> bool504 pub fn check_copy_overlap(
505     src_origin: &CLVec<usize>,
506     src_offset: usize,
507     dst_origin: &CLVec<usize>,
508     dst_offset: usize,
509     region: &CLVec<usize>,
510     row_pitch: usize,
511     slice_pitch: usize,
512 ) -> bool {
513     let slice_size = (region[1] - 1) * row_pitch + region[0];
514     let block_size = (region[2] - 1) * slice_pitch + slice_size;
515     let src_start =
516         src_origin[2] * slice_pitch + src_origin[1] * row_pitch + src_origin[0] + src_offset;
517     let src_end = src_start + block_size;
518     let dst_start =
519         dst_origin[2] * slice_pitch + dst_origin[1] * row_pitch + dst_origin[0] + dst_offset;
520     let dst_end = dst_start + block_size;
521 
522     /* No overlap if dst ends before src starts or if src ends
523      * before dst starts.
524      */
525     if (dst_end <= src_start) || (src_end <= dst_start) {
526         return false;
527     }
528 
529     /* No overlap if region[0] for dst or src fits in the gap
530      * between region[0] and row_pitch.
531      */
532     {
533         let src_dx = (src_origin[0] + src_offset) % row_pitch;
534         let dst_dx = (dst_origin[0] + dst_offset) % row_pitch;
535         if ((dst_dx >= src_dx + region[0]) && (dst_dx + region[0] <= src_dx + row_pitch))
536             || ((src_dx >= dst_dx + region[0]) && (src_dx + region[0] <= dst_dx + row_pitch))
537         {
538             return false;
539         }
540     }
541 
542     /* No overlap if region[1] for dst or src fits in the gap
543      * between region[1] and slice_pitch.
544      */
545     {
546         let src_dy = (src_origin[1] * row_pitch + src_origin[0] + src_offset) % slice_pitch;
547         let dst_dy = (dst_origin[1] * row_pitch + dst_origin[0] + dst_offset) % slice_pitch;
548         if ((dst_dy >= src_dy + slice_size) && (dst_dy + slice_size <= src_dy + slice_pitch))
549             || ((src_dy >= dst_dy + slice_size) && (src_dy + slice_size <= dst_dy + slice_pitch))
550         {
551             return false;
552         }
553     }
554 
555     /* Otherwise src and dst overlap. */
556     true
557 }
558 
559 pub mod cl_slice {
560     use crate::api::util::CLResult;
561     use mesa_rust_util::ptr::addr;
562     use rusticl_opencl_gen::CL_INVALID_VALUE;
563     use std::mem;
564     use std::slice;
565 
566     /// Wrapper around [`std::slice::from_raw_parts`] that returns `Err(CL_INVALID_VALUE)` if any of these conditions is met:
567     /// - `data` is null
568     /// - `data` is not correctly aligned for `T`
569     /// - `len * std::mem::size_of::<T>()` is larger than `isize::MAX`
570     /// - `data` + `len * std::mem::size_of::<T>()` wraps around the address space
571     ///
572     /// # Safety
573     /// The behavior is undefined if any of the other requirements imposed by
574     /// [`std::slice::from_raw_parts`] is violated.
575     #[inline]
from_raw_parts<'a, T>(data: *const T, len: usize) -> CLResult<&'a [T]>576     pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> CLResult<&'a [T]> {
577         if allocation_obviously_invalid(data, len) {
578             return Err(CL_INVALID_VALUE);
579         }
580 
581         // SAFETY: We've checked that `data` is not null and properly aligned. We've also checked
582         // that the total size in bytes does not exceed `isize::MAX` and that adding that size to
583         // `data` does not wrap around the address space.
584         //
585         // The caller has to uphold the other safety requirements imposed by [`std::slice::from_raw_parts`].
586         unsafe { Ok(slice::from_raw_parts(data, len)) }
587     }
588 
589     /// Wrapper around [`std::slice::from_raw_parts_mut`] that returns `Err(CL_INVALID_VALUE)` if any of these conditions is met:
590     /// - `data` is null
591     /// - `data` is not correctly aligned for `T`
592     /// - `len * std::mem::size_of::<T>()` is larger than `isize::MAX`
593     /// - `data` + `len * std::mem::size_of::<T>()` wraps around the address space
594     ///
595     /// # Safety
596     /// The behavior is undefined if any of the other requirements imposed by
597     /// [`std::slice::from_raw_parts_mut`] is violated.
598     #[inline]
from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> CLResult<&'a mut [T]>599     pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> CLResult<&'a mut [T]> {
600         if allocation_obviously_invalid(data, len) {
601             return Err(CL_INVALID_VALUE);
602         }
603 
604         // SAFETY: We've checked that `data` is not null and properly aligned. We've also checked
605         // that the total size in bytes does not exceed `isize::MAX` and that adding that size to
606         // `data` does not wrap around the address space.
607         //
608         // The caller has to uphold the other safety requirements imposed by [`std::slice::from_raw_parts_mut`].
609         unsafe { Ok(slice::from_raw_parts_mut(data, len)) }
610     }
611 
612     #[must_use]
allocation_obviously_invalid<T>(data: *const T, len: usize) -> bool613     fn allocation_obviously_invalid<T>(data: *const T, len: usize) -> bool {
614         let Some(total_size) = mem::size_of::<T>().checked_mul(len) else {
615             return true;
616         };
617         data.is_null()
618             || !mesa_rust_util::ptr::is_aligned(data)
619             || total_size > isize::MAX as usize
620             || addr(data).checked_add(total_size).is_none()
621     }
622 }
623