1use alloc::format;
2use alloc::string::String;
3use alloc::sync::Arc;
4use core::{
5 any::{Any, TypeId},
6 fmt::Display,
7 hash::{Hash, Hasher},
8};
9use cubecl_common::{
10 format::{DebugRaw, format_str},
11 hash::{StableHash, StableHasher},
12};
13use cubecl_ir::{
14 AddressType,
15 settings::{Dim3, ExecutionMode},
16};
17use derive_more::{Eq, PartialEq};
18
19#[macro_export(local_inner_macros)]
20macro_rules! storage_id_type {
22 ($name:ident) => {
23 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, PartialOrd, Ord)]
25 pub struct $name {
26 value: usize,
27 }
28
29 impl $name {
30 pub fn new() -> Self {
32 use core::sync::atomic::{AtomicUsize, Ordering};
33
34 static COUNTER: AtomicUsize = AtomicUsize::new(0);
35
36 let value = COUNTER.fetch_add(1, Ordering::Relaxed);
37 if value == usize::MAX {
38 core::panic!("Memory ID overflowed");
39 }
40 Self { value }
41 }
42 }
43
44 impl Default for $name {
45 fn default() -> Self {
46 Self::new()
47 }
48 }
49 };
50}
51
52#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, PartialOrd, Ord)]
63pub struct GraphId {
64 value: u64,
65}
66
67impl GraphId {
68 pub fn new() -> Self {
70 use core::sync::atomic::{AtomicU64, Ordering};
71
72 static COUNTER: AtomicU64 = AtomicU64::new(0);
73
74 let value = COUNTER.fetch_add(1, Ordering::Relaxed);
75 if value == u64::MAX {
76 core::panic!("Graph ID overflowed");
77 }
78 Self { value }
79 }
80}
81
82impl Default for GraphId {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88#[derive(Clone, PartialEq, Eq)]
90pub struct KernelId {
91 #[eq(skip)]
92 type_name: &'static str,
93 pub(crate) type_id: core::any::TypeId,
94 pub cube_dim: Dim3,
96 pub address_type: AddressType,
98 pub mode: ExecutionMode,
100 pub(crate) info: Option<Info>,
101}
102
103impl Hash for KernelId {
104 fn hash<H: Hasher>(&self, state: &mut H) {
105 self.type_id.hash(state);
106 self.address_type.hash(state);
107 self.cube_dim.hash(state);
108 self.mode.hash(state);
109 self.info.hash(state);
110 }
111}
112
113impl core::fmt::Debug for KernelId {
114 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
115 let mut debug_str = f.debug_struct("KernelId");
116 debug_str
117 .field("type", &DebugRaw(self.type_name))
118 .field("address_type", &self.address_type);
119 debug_str.field("cube_dim", &self.cube_dim);
120 debug_str.field("mode", &self.mode);
121 match &self.info {
122 Some(info) => debug_str.field("info", info),
123 None => debug_str.field("info", &self.info),
124 };
125 debug_str.finish()
126 }
127}
128
129impl Display for KernelId {
130 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
131 match &self.info {
132 Some(info) => f.write_str(
133 format_str(
134 format!("{info:?}").as_str(),
135 &[('(', ')'), ('[', ']'), ('{', '}')],
136 true,
137 )
138 .as_str(),
139 ),
140 None => f.write_str("No info"),
141 }
142 }
143}
144
145impl KernelId {
146 pub fn short_name(&self) -> &'static str {
149 let name = self.type_name.split('<').next().unwrap_or(self.type_name);
150 name.rsplit("::").next().unwrap_or(name)
151 }
152
153 pub fn type_name(&self) -> &'static str {
156 self.type_name
157 }
158
159 pub fn new<T: 'static>() -> Self {
161 Self {
162 type_id: core::any::TypeId::of::<T>(),
163 type_name: core::any::type_name::<T>(),
164 info: None,
165 cube_dim: Dim3::new_single(),
166 mode: ExecutionMode::Checked,
167 address_type: Default::default(),
168 }
169 }
170
171 pub fn stable_format(&self) -> String {
175 format!(
176 "{}-{}-{:?}-{:?}-{:?}",
177 self.type_name, self.address_type, self.cube_dim, self.mode, self.info
178 )
179 }
180
181 pub fn stable_hash(&self) -> StableHash {
185 let mut hasher = StableHasher::new();
186 self.type_name.hash(&mut hasher);
187 self.address_type.hash(&mut hasher);
188 self.cube_dim.hash(&mut hasher);
189 self.mode.hash(&mut hasher);
190 self.info.hash(&mut hasher);
191
192 hasher.finalize()
193 }
194
195 pub fn entrypoint_name(&self, base: &str) -> String {
197 format!("{base}_{:08x}", self.stable_hash() as u32)
198 }
199
200 pub fn info<I: 'static + PartialEq + Eq + Hash + core::fmt::Debug + Send + Sync>(
205 mut self,
206 info: I,
207 ) -> Self {
208 self.info = Some(Info::new(info));
209 self
210 }
211
212 pub fn mode(mut self, mode: ExecutionMode) -> Self {
214 self.mode = mode;
215 self
216 }
217
218 pub fn cube_dim(mut self, cube_dim: Dim3) -> Self {
220 self.cube_dim = cube_dim;
221 self
222 }
223
224 pub fn address_type(mut self, addr_ty: AddressType) -> Self {
226 self.address_type = addr_ty;
227 self
228 }
229}
230
231impl core::fmt::Debug for Info {
232 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
233 self.value.fmt(f)
234 }
235}
236
237impl Info {
238 fn new<T: 'static + PartialEq + Eq + Hash + core::fmt::Debug + Send + Sync>(id: T) -> Self {
239 Self {
240 value: Arc::new(id),
241 }
242 }
243}
244
245trait DynKey: core::fmt::Debug + Send + Sync {
251 fn dyn_type_id(&self) -> TypeId;
252 fn dyn_eq(&self, other: &dyn DynKey) -> bool;
253 fn dyn_hash(&self, state: &mut dyn Hasher);
254 fn dyn_hash_one(&self) -> StableHash;
255 fn as_any(&self) -> &dyn Any;
256}
257
258impl PartialEq for Info {
259 fn eq(&self, other: &Self) -> bool {
260 self.value.dyn_eq(other.value.as_ref())
261 }
262}
263
264#[derive(Clone)]
266pub(crate) struct Info {
267 value: Arc<dyn DynKey>,
268}
269impl Eq for Info {}
270
271impl Hash for Info {
272 fn hash<H: Hasher>(&self, state: &mut H) {
273 self.value.dyn_type_id().hash(state);
274 self.value.dyn_hash(state)
275 }
276}
277
278impl<T: 'static + PartialEq + Eq + Hash + core::fmt::Debug + Send + Sync> DynKey for T {
279 fn dyn_eq(&self, other: &dyn DynKey) -> bool {
280 if let Some(other) = other.as_any().downcast_ref::<T>() {
281 self == other
282 } else {
283 false
284 }
285 }
286
287 fn dyn_type_id(&self) -> TypeId {
288 TypeId::of::<T>()
289 }
290
291 fn dyn_hash(&self, state: &mut dyn Hasher) {
292 let hash = self.dyn_hash_one();
293 state.write_u128(hash);
294 }
295
296 fn dyn_hash_one(&self) -> StableHash {
297 let mut hasher = StableHasher::new();
298 self.hash(&mut hasher);
299 hasher.finalize()
300 }
301
302 fn as_any(&self) -> &dyn Any {
303 self
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use std::collections::HashSet;
311
312 #[test_log::test]
313 pub fn kernel_id_hash() {
314 let value_1 = KernelId::new::<()>().info("1");
315 let value_2 = KernelId::new::<()>().info("2");
316
317 let mut set = HashSet::new();
318
319 set.insert(value_1.clone());
320
321 assert!(set.contains(&value_1));
322 assert!(!set.contains(&value_2));
323 }
324}