xref: /linux/drivers/gpu/nova-core/num.rs (revision 38f7e5450ebfc6f2e046a249a3f629ea7bec8c31)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Numerical helpers functions and traits.
4 //!
5 //! This is essentially a staging module for code to mature until it can be moved to the `kernel`
6 //! crate.
7 
8 use kernel::{
9     macros::paste,
10     prelude::*, //
11 };
12 
13 /// Implements safe `as` conversion functions from a given type into a series of target types.
14 ///
15 /// These functions can be used in place of `as`, with the guarantee that they will be lossless.
16 macro_rules! impl_safe_as {
17     ($from:ty as { $($into:ty),* }) => {
18         $(
19         paste! {
20             #[doc = ::core::concat!(
21                 "Losslessly converts a [`",
22                 ::core::stringify!($from),
23                 "`] into a [`",
24                 ::core::stringify!($into),
25                 "`].")]
26             ///
27             /// This conversion is allowed as it is always lossless. Prefer this over the `as`
28             /// keyword to ensure no lossy casts are performed.
29             ///
30             /// This is for use from a `const` context. For non `const` use, prefer the
31             /// [`FromSafeCast`] and [`IntoSafeCast`] traits.
32             ///
33             /// # Examples
34             ///
35             /// ```
36             /// use crate::num;
37             ///
38             #[doc = ::core::concat!(
39                 "assert_eq!(num::",
40                 ::core::stringify!($from),
41                 "_as_",
42                 ::core::stringify!($into),
43                 "(1",
44                 ::core::stringify!($from),
45                 "), 1",
46                 ::core::stringify!($into),
47                 ");")]
48             /// ```
49             #[allow(unused)]
50             #[inline(always)]
51             pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into {
52                 kernel::static_assert!(size_of::<$into>() >= size_of::<$from>());
53 
54                 value as $into
55             }
56         }
57         )*
58     };
59 }
60 
61 impl_safe_as!(u8 as { u16, u32, u64, usize });
62 impl_safe_as!(u16 as { u32, u64, usize });
63 impl_safe_as!(u32 as { u64, usize } );
64 // `u64` and `usize` have the same size on 64-bit platforms.
65 #[cfg(CONFIG_64BIT)]
66 impl_safe_as!(u64 as { usize } );
67 
68 // A `usize` fits into a `u64` on 32 and 64-bit platforms.
69 #[cfg(any(CONFIG_32BIT, CONFIG_64BIT))]
70 impl_safe_as!(usize as { u64 });
71 
72 // A `usize` fits into a `u32` on 32-bit platforms.
73 #[cfg(CONFIG_32BIT)]
74 impl_safe_as!(usize as { u32 });
75 
76 /// Extension trait providing guaranteed lossless cast to `Self` from `T`.
77 ///
78 /// The standard library's `From` implementations do not cover conversions that are not portable or
79 /// future-proof. For instance, even though it is safe today, `From<usize>` is not implemented for
80 /// [`u64`] because of the possibility to support larger-than-64bit architectures in the future.
81 ///
82 /// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that
83 /// technically cannot fail, or to use the `as` keyword, which can silently strip data if the
84 /// destination type is smaller than the source.
85 ///
86 /// Both options are hardly acceptable for the kernel. It is also a much more architecture
87 /// dependent environment, supporting only 32 and 64 bit architectures, with some modules
88 /// explicitly depending on a specific bus width that could greatly benefit from infallible
89 /// conversion operations.
90 ///
91 /// Thus this extension trait that provides, for the architecture the kernel is built for, safe
92 /// conversion between types for which such cast is lossless.
93 ///
94 /// In other words, this trait is implemented if, for the current build target and with `t: T`, the
95 /// `t as Self` operation is completely lossless.
96 ///
97 /// Prefer this over the `as` keyword to ensure no lossy casts are performed.
98 ///
99 /// If you need to perform a conversion in `const` context, use [`u64_as_usize`], [`u32_as_usize`],
100 /// [`usize_as_u64`], etc.
101 ///
102 /// # Examples
103 ///
104 /// ```
105 /// use crate::num::FromSafeCast;
106 ///
107 /// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00u32 as usize);
108 /// ```
109 pub(crate) trait FromSafeCast<T> {
110     /// Create a `Self` from `value`. This operation is guaranteed to be lossless.
111     fn from_safe_cast(value: T) -> Self;
112 }
113 
114 impl FromSafeCast<usize> for u64 {
115     fn from_safe_cast(value: usize) -> Self {
116         usize_as_u64(value)
117     }
118 }
119 
120 #[cfg(CONFIG_32BIT)]
121 impl FromSafeCast<usize> for u32 {
122     fn from_safe_cast(value: usize) -> Self {
123         usize_as_u32(value)
124     }
125 }
126 
127 impl FromSafeCast<u32> for usize {
128     fn from_safe_cast(value: u32) -> Self {
129         u32_as_usize(value)
130     }
131 }
132 
133 #[cfg(CONFIG_64BIT)]
134 impl FromSafeCast<u64> for usize {
135     fn from_safe_cast(value: u64) -> Self {
136         u64_as_usize(value)
137     }
138 }
139 
140 /// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]
141 /// is to [`From`].
142 ///
143 /// See the documentation of [`FromSafeCast`] for the motivation.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use crate::num::IntoSafeCast;
149 ///
150 /// assert_eq!(0xf00u32.into_safe_cast(), 0xf00u32 as usize);
151 /// ```
152 pub(crate) trait IntoSafeCast<T> {
153     /// Convert `self` into a `T`. This operation is guaranteed to be lossless.
154     fn into_safe_cast(self) -> T;
155 }
156 
157 /// Reverse operation for types implementing [`FromSafeCast`].
158 impl<S, T> IntoSafeCast<T> for S
159 where
160     T: FromSafeCast<S>,
161 {
162     fn into_safe_cast(self) -> T {
163         T::from_safe_cast(self)
164     }
165 }
166 
167 /// Implements lossless conversion of a constant from a larger type into a smaller one.
168 macro_rules! impl_const_into {
169     ($from:ty => { $($into:ty),* }) => {
170         $(
171         paste! {
172             #[doc = ::core::concat!(
173                 "Performs a build-time safe conversion of a [`",
174                 ::core::stringify!($from),
175                 "`] constant value into a [`",
176                 ::core::stringify!($into),
177                 "`].")]
178             ///
179             /// This checks at compile-time that the conversion is lossless, and triggers a build
180             /// error if it isn't.
181             ///
182             /// # Examples
183             ///
184             /// ```
185             /// use crate::num;
186             ///
187             /// // Succeeds because the value of the source fits into the destination's type.
188             #[doc = ::core::concat!(
189                 "assert_eq!(num::",
190                 ::core::stringify!($from),
191                 "_into_",
192                 ::core::stringify!($into),
193                 "::<1",
194                 ::core::stringify!($from),
195                 ">(), 1",
196                 ::core::stringify!($into),
197                 ");")]
198             /// ```
199             #[allow(unused)]
200             pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into {
201                 // Make sure that the target type is smaller than the source one.
202                 static_assert!($from::BITS >= $into::BITS);
203                 // CAST: we statically enforced above that `$from` is larger than `$into`, so the
204                 // `as` conversion will be lossless.
205                 build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
206 
207                 N as $into
208             }
209         }
210         )*
211     };
212 }
213 
214 impl_const_into!(usize => { u8, u16, u32 });
215 impl_const_into!(u64 => { u8, u16, u32 });
216 impl_const_into!(u32 => { u8, u16 });
217 impl_const_into!(u16 => { u8 });
218 
219 /// Creates an enum type associated to a [`Bounded`](kernel::num::Bounded), with a [`From`]
220 /// conversion to the associated `Bounded` and either a [`TryFrom`] or `From` conversion from the
221 /// associated `Bounded`.
222 // TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros
223 // once they land.
224 #[macro_export]
225 macro_rules! bounded_enum {
226     (
227         $(#[$enum_meta:meta])*
228         $vis:vis enum $enum_type:ident with $from_impl:ident<Bounded<$width:ty, $length:literal>> {
229             $( $(#[doc = $variant_doc:expr])* $variant:ident = $value:expr),* $(,)*
230         }
231     ) => {
232         $(#[$enum_meta])*
233         $vis enum $enum_type {
234             $(
235                 $(#[doc = $variant_doc])*
236                 $variant = $value
237             ),*
238         }
239 
240         impl core::convert::From<$enum_type> for kernel::num::Bounded<$width, $length> {
241             fn from(value: $enum_type) -> Self {
242                 match value {
243                     $($enum_type::$variant =>
244                         kernel::num::Bounded::<$width, _>::new::<{ $value }>()),*
245                 }
246             }
247         }
248 
249         bounded_enum!(@impl_from $enum_type with $from_impl<Bounded<$width, $length>> {
250             $($variant = $value),*
251         });
252     };
253 
254     // `TryFrom` implementation from associated `Bounded` to enum type.
255     (@impl_from $enum_type:ident with TryFrom<Bounded<$width:ty, $length:literal>> {
256         $($variant:ident = $value:expr),* $(,)*
257     }) => {
258         impl core::convert::TryFrom<kernel::num::Bounded<$width, $length>> for $enum_type {
259             type Error = kernel::error::Error;
260 
261             fn try_from(
262                 value: kernel::num::Bounded<$width, $length>
263             ) -> kernel::error::Result<Self> {
264                 match value.get() {
265                     $(
266                         $value => Ok($enum_type::$variant),
267                     )*
268                     _ => Err(kernel::error::code::EINVAL),
269                 }
270             }
271         }
272     };
273 
274     // `From` implementation from associated `Bounded` to enum type. Triggers a build-time error if
275     // all possible values of the `Bounded` are not covered by the enum type.
276     (@impl_from $enum_type:ident with From<Bounded<$width:ty, $length:literal>> {
277         $($variant:ident = $value:expr),* $(,)*
278     }) => {
279         impl core::convert::From<kernel::num::Bounded<$width, $length>> for $enum_type {
280             fn from(value: kernel::num::Bounded<$width, $length>) -> Self {
281                 const MAX: $width = 1 << $length;
282 
283                 // Makes the compiler optimizer aware of the possible range of values.
284                 let value = value.get() & ((1 << $length) - 1);
285                 match value {
286                     $(
287                         $value => $enum_type::$variant,
288                     )*
289                     // PANIC: we cannot reach this arm as all possible variants are handled by the
290                     // match arms above. It is here to make the compiler complain if `$enum_type`
291                     // does not cover all values of the `0..MAX` range.
292                     MAX.. => unreachable!(),
293                 }
294             }
295         }
296     }
297 }
298