1use core::{fmt::Display, hash::Hash};
2
3use crate::{AddressSpace, FloatKind, IntKind, StorageType, TypeHash};
4
5use super::{ElemType, Type, UIntKind};
6use cubecl_common::{e2m1, e4m3, e5m2, ue8m0};
7use derive_more::From;
8use float_ord::FloatOrd;
9
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[derive(Debug, Clone, Copy, TypeHash, Hash, PartialEq, Eq, PartialOrd, Ord)]
12#[allow(missing_docs)]
13pub struct Value {
14 pub kind: ValueKind,
15 pub ty: Type,
16}
17
18impl Value {
19 pub fn new(id: Id, ty: Type) -> Self {
20 Self {
21 kind: ValueKind::Value { id },
22 ty,
23 }
24 }
25
26 pub fn constant(value: ConstantValue, ty: impl Into<Type>) -> Self {
27 let ty = ty.into();
28 let value = value.cast_to(ty);
29 Self {
30 kind: ValueKind::Constant(value),
31 ty,
32 }
33 }
34
35 pub fn elem_type(&self) -> ElemType {
36 self.ty.elem_type()
37 }
38
39 pub fn storage_type(&self) -> StorageType {
40 self.ty.storage_type()
41 }
42
43 pub fn can_mutate(&self) -> bool {
44 self.ty.is_ptr()
45 }
46
47 pub fn address_space(&self) -> AddressSpace {
48 match self.ty {
49 Type::Pointer(_, addr_space) => addr_space,
50 _ => match self.kind {
51 ValueKind::Value { .. } | ValueKind::Constant(..) => AddressSpace::Local,
52 },
53 }
54 }
55
56 pub fn value_type(&self) -> Type {
57 self.ty.value_type()
58 }
59}
60
61pub type Id = u32;
62
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64#[derive(Debug, Clone, Copy, TypeHash, PartialEq, Eq, Hash, PartialOrd, Ord)]
65pub enum ValueKind {
66 Value { id: Id },
67 Constant(ConstantValue),
68}
69
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TypeHash, PartialOrd, Ord)]
72#[repr(u32)]
73pub enum Builtin {
74 UnitPos,
75 UnitPosX,
76 UnitPosY,
77 UnitPosZ,
78 CubePosCluster,
79 CubePosClusterX,
80 CubePosClusterY,
81 CubePosClusterZ,
82 CubePos,
83 CubePosX,
84 CubePosY,
85 CubePosZ,
86 CubeDim,
87 CubeDimX,
88 CubeDimY,
89 CubeDimZ,
90 CubeClusterDim,
91 CubeClusterDimX,
92 CubeClusterDimY,
93 CubeClusterDimZ,
94 CubeCount,
95 CubeCountX,
96 CubeCountY,
97 CubeCountZ,
98 PlaneDim,
99 PlanePos,
100 UnitPosPlane,
101 AbsolutePos,
102 AbsolutePosX,
103 AbsolutePosY,
104 AbsolutePosZ,
105}
106
107impl Value {
108 pub fn is_immutable(&self) -> bool {
111 !self.can_mutate()
112 }
113
114 pub fn is_array_like(&self) -> bool {
117 self.ty.is_array_like()
118 }
119
120 pub fn is_value(&self) -> bool {
121 self.ty.is_value()
122 }
123
124 pub fn is_memory(&self) -> bool {
127 matches!(
128 self.address_space(),
129 AddressSpace::Global(_) | AddressSpace::Shared
130 )
131 }
132
133 pub fn has_buffer_length(&self) -> bool {
134 matches!(self.address_space(), AddressSpace::Global(_))
135 }
136
137 pub fn is_constant(&self, value: i64) -> bool {
139 match self.kind {
140 ValueKind::Constant(ConstantValue::Int(val)) => val == value,
141 ValueKind::Constant(ConstantValue::UInt(val)) => val as i64 == value,
142 ValueKind::Constant(ConstantValue::Float(val)) => val == value as f64,
143 _ => false,
144 }
145 }
146
147 pub fn is_true(&self) -> bool {
149 match self.kind {
150 ValueKind::Constant(ConstantValue::Bool(val)) => val,
151 _ => false,
152 }
153 }
154
155 pub fn is_false(&self) -> bool {
157 match self.kind {
158 ValueKind::Constant(ConstantValue::Bool(val)) => !val,
159 _ => false,
160 }
161 }
162}
163
164#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168#[derive(Debug, Clone, Copy, TypeHash, PartialEq, PartialOrd, From)]
169#[allow(missing_docs, clippy::derive_ord_xor_partial_ord)]
170pub enum ConstantValue {
171 Int(i64),
172 Float(f64),
173 UInt(u64),
174 Bool(bool),
175}
176
177impl Ord for ConstantValue {
178 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
179 match (self, other) {
182 (ConstantValue::Float(this), ConstantValue::Float(other)) => {
183 FloatOrd(*this).cmp(&FloatOrd(*other))
184 }
185 _ => self.partial_cmp(other).unwrap(),
186 }
187 }
188}
189
190impl Eq for ConstantValue {}
191impl Hash for ConstantValue {
192 fn hash<H: core::hash::Hasher>(&self, ra_expand_state: &mut H) {
193 core::mem::discriminant(self).hash(ra_expand_state);
194 match self {
195 ConstantValue::Int(f0) => {
196 f0.hash(ra_expand_state);
197 }
198 ConstantValue::Float(f0) => {
199 FloatOrd(*f0).hash(ra_expand_state);
200 }
201 ConstantValue::UInt(f0) => {
202 f0.hash(ra_expand_state);
203 }
204 ConstantValue::Bool(f0) => {
205 f0.hash(ra_expand_state);
206 }
207 }
208 }
209}
210
211impl ConstantValue {
212 pub fn try_as_usize(&self) -> Option<usize> {
216 match self {
217 ConstantValue::UInt(val) => Some(*val as usize),
218 ConstantValue::Int(val) => Some(*val as usize),
219 ConstantValue::Float(_) => None,
220 ConstantValue::Bool(_) => None,
221 }
222 }
223
224 pub fn as_usize(&self) -> usize {
226 match self {
227 ConstantValue::UInt(val) => *val as usize,
228 ConstantValue::Int(val) => *val as usize,
229 ConstantValue::Float(val) => *val as usize,
230 ConstantValue::Bool(val) => *val as usize,
231 }
232 }
233
234 pub fn try_as_u32(&self) -> Option<u32> {
238 self.try_as_u64().map(|it| it as u32)
239 }
240
241 pub fn as_u32(&self) -> u32 {
245 self.as_u64() as u32
246 }
247
248 pub fn try_as_u64(&self) -> Option<u64> {
252 match self {
253 ConstantValue::UInt(val) => Some(*val),
254 ConstantValue::Int(val) => Some(*val as u64),
255 ConstantValue::Float(_) => None,
256 ConstantValue::Bool(_) => None,
257 }
258 }
259
260 pub fn as_u64(&self) -> u64 {
262 match self {
263 ConstantValue::UInt(val) => *val,
264 ConstantValue::Int(val) => *val as u64,
265 ConstantValue::Float(val) => *val as u64,
266 ConstantValue::Bool(val) => *val as u64,
267 }
268 }
269
270 pub fn try_as_i64(&self) -> Option<i64> {
274 match self {
275 ConstantValue::UInt(val) => Some(*val as i64),
276 ConstantValue::Int(val) => Some(*val),
277 ConstantValue::Float(_) => None,
278 ConstantValue::Bool(_) => None,
279 }
280 }
281
282 pub fn as_i128(&self) -> i128 {
284 match self {
285 ConstantValue::UInt(val) => *val as i128,
286 ConstantValue::Int(val) => *val as i128,
287 ConstantValue::Float(val) => *val as i128,
288 ConstantValue::Bool(val) => *val as i128,
289 }
290 }
291
292 pub fn as_i64(&self) -> i64 {
294 match self {
295 ConstantValue::UInt(val) => *val as i64,
296 ConstantValue::Int(val) => *val,
297 ConstantValue::Float(val) => *val as i64,
298 ConstantValue::Bool(val) => *val as i64,
299 }
300 }
301
302 pub fn as_i32(&self) -> i32 {
304 match self {
305 ConstantValue::UInt(val) => *val as i32,
306 ConstantValue::Int(val) => *val as i32,
307 ConstantValue::Float(val) => *val as i32,
308 ConstantValue::Bool(val) => *val as i32,
309 }
310 }
311
312 pub fn try_as_f64(&self) -> Option<f64> {
316 match self {
317 ConstantValue::Float(val) => Some(*val),
318 _ => None,
319 }
320 }
321
322 pub fn as_f64(&self) -> f64 {
324 match self {
325 ConstantValue::UInt(val) => *val as f64,
326 ConstantValue::Int(val) => *val as f64,
327 ConstantValue::Float(val) => *val,
328 ConstantValue::Bool(val) => *val as u8 as f64,
329 }
330 }
331
332 pub fn try_as_bool(&self) -> Option<bool> {
334 match self {
335 ConstantValue::Bool(val) => Some(*val),
336 _ => None,
337 }
338 }
339
340 pub fn as_bool(&self) -> bool {
344 match self {
345 ConstantValue::UInt(val) => *val != 0,
346 ConstantValue::Int(val) => *val != 0,
347 ConstantValue::Float(val) => *val != 0.,
348 ConstantValue::Bool(val) => *val,
349 }
350 }
351
352 pub fn is_zero(&self) -> bool {
353 match self {
354 ConstantValue::Int(val) => *val == 0,
355 ConstantValue::Float(val) => *val == 0.0,
356 ConstantValue::UInt(val) => *val == 0,
357 ConstantValue::Bool(val) => !*val,
358 }
359 }
360
361 pub fn is_one(&self) -> bool {
362 match self {
363 ConstantValue::Int(val) => *val == 1,
364 ConstantValue::Float(val) => *val == 1.0,
365 ConstantValue::UInt(val) => *val == 1,
366 ConstantValue::Bool(val) => *val,
367 }
368 }
369
370 pub fn cast_to(&self, other: impl Into<Type>) -> ConstantValue {
371 match other.into().storage_type() {
372 StorageType::Scalar(elem_type) => match elem_type {
373 ElemType::Float(kind) => match kind {
374 FloatKind::E2M1 => e2m1::from_f64(self.as_f64()).to_f64(),
375 FloatKind::E2M3 | FloatKind::E3M2 => {
376 unimplemented!("FP6 constants not yet supported")
377 }
378 FloatKind::E4M3 => e4m3::from_f64(self.as_f64()).to_f64(),
379 FloatKind::E5M2 => e5m2::from_f64(self.as_f64()).to_f64(),
380 FloatKind::UE8M0 => ue8m0::from_f64(self.as_f64()).to_f64(),
381 FloatKind::F16 => half::f16::from_f64(self.as_f64()).to_f64(),
382 FloatKind::BF16 => half::bf16::from_f64(self.as_f64()).to_f64(),
383 FloatKind::Flex32 | FloatKind::TF32 | FloatKind::F32 => {
384 self.as_f64() as f32 as f64
385 }
386 FloatKind::F64 => self.as_f64(),
387 }
388 .into(),
389 ElemType::Int(kind) => match kind {
390 IntKind::I8 => self.as_i64() as i8 as i64,
391 IntKind::I16 => self.as_i64() as i16 as i64,
392 IntKind::I32 => self.as_i64() as i32 as i64,
393 IntKind::I64 => self.as_i64(),
394 }
395 .into(),
396 ElemType::UInt(kind) => match kind {
397 UIntKind::U8 => self.as_u64() as u8 as u64,
398 UIntKind::U16 => self.as_u64() as u16 as u64,
399 UIntKind::U32 => self.as_u64() as u32 as u64,
400 UIntKind::U64 => self.as_u64(),
401 }
402 .into(),
403 ElemType::Bool => self.as_bool().into(),
404 },
405 StorageType::Packed(ElemType::Float(FloatKind::E2M1), 2) => {
406 e2m1::from_f64(self.as_f64()).to_f64().into()
407 }
408 StorageType::Packed(..) => unimplemented!("Unsupported packed type"),
409 }
410 }
411}
412
413impl Display for ConstantValue {
414 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
415 match self {
416 ConstantValue::Int(val) => write!(f, "{val}"),
417 ConstantValue::Float(val) => write!(f, "{val:?}"),
418 ConstantValue::UInt(val) => write!(f, "{val}"),
419 ConstantValue::Bool(val) => write!(f, "{val}"),
420 }
421 }
422}
423
424impl Value {
425 pub fn vector_size(&self) -> usize {
426 self.ty.vector_size()
427 }
428
429 pub fn id(&self) -> Id {
430 match self.kind {
431 ValueKind::Value { id, .. } => id,
432 _ => panic!("Can't get ID of constant"),
433 }
434 }
435
436 pub fn as_const(&self) -> Option<ConstantValue> {
437 match self.kind {
438 ValueKind::Constant(constant) => Some(constant),
439 _ => None,
440 }
441 }
442}
443
444impl Display for Value {
445 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
446 match self.kind {
447 ValueKind::Constant(constant) => write!(f, "{}({constant})", self.ty),
448 other => write!(f, "{other}"),
449 }
450 }
451}
452
453impl Display for ValueKind {
454 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
455 match self {
456 ValueKind::Constant(constant) => write!(f, "{constant}"),
457 ValueKind::Value { id } => write!(f, "%{id}"),
458 }
459 }
460}
461
462impl From<&Value> for Value {
464 fn from(value: &Value) -> Self {
465 *value
466 }
467}