1use crate::ir;
8
9use super::TypeResolution;
10
11impl crate::ScalarKind {
12    pub const fn is_numeric(self) -> bool {
13        match self {
14            crate::ScalarKind::Sint
15            | crate::ScalarKind::Uint
16            | crate::ScalarKind::Float
17            | crate::ScalarKind::AbstractInt
18            | crate::ScalarKind::AbstractFloat => true,
19            crate::ScalarKind::Bool => false,
20        }
21    }
22}
23
24impl crate::Scalar {
25    pub const I32: Self = Self {
26        kind: crate::ScalarKind::Sint,
27        width: 4,
28    };
29    pub const U32: Self = Self {
30        kind: crate::ScalarKind::Uint,
31        width: 4,
32    };
33    pub const F16: Self = Self {
34        kind: crate::ScalarKind::Float,
35        width: 2,
36    };
37    pub const F32: Self = Self {
38        kind: crate::ScalarKind::Float,
39        width: 4,
40    };
41    pub const F64: Self = Self {
42        kind: crate::ScalarKind::Float,
43        width: 8,
44    };
45    pub const I64: Self = Self {
46        kind: crate::ScalarKind::Sint,
47        width: 8,
48    };
49    pub const U64: Self = Self {
50        kind: crate::ScalarKind::Uint,
51        width: 8,
52    };
53    pub const BOOL: Self = Self {
54        kind: crate::ScalarKind::Bool,
55        width: crate::BOOL_WIDTH,
56    };
57    pub const ABSTRACT_INT: Self = Self {
58        kind: crate::ScalarKind::AbstractInt,
59        width: crate::ABSTRACT_WIDTH,
60    };
61    pub const ABSTRACT_FLOAT: Self = Self {
62        kind: crate::ScalarKind::AbstractFloat,
63        width: crate::ABSTRACT_WIDTH,
64    };
65
66    pub const fn is_abstract(self) -> bool {
67        match self.kind {
68            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true,
69            crate::ScalarKind::Sint
70            | crate::ScalarKind::Uint
71            | crate::ScalarKind::Float
72            | crate::ScalarKind::Bool => false,
73        }
74    }
75
76    pub const fn float(width: crate::Bytes) -> Self {
81        Self {
82            kind: crate::ScalarKind::Float,
83            width,
84        }
85    }
86
87    pub const fn to_inner_scalar(self) -> crate::TypeInner {
88        crate::TypeInner::Scalar(self)
89    }
90
91    pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner {
92        crate::TypeInner::Vector { size, scalar: self }
93    }
94
95    pub const fn to_inner_atomic(self) -> crate::TypeInner {
96        crate::TypeInner::Atomic(self)
97    }
98}
99
100const POINTER_SPAN: u32 = 4;
101
102impl crate::TypeInner {
103    pub const fn scalar(&self) -> Option<crate::Scalar> {
114        use crate::TypeInner as Ti;
115        match *self {
116            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar),
117            Ti::Matrix { scalar, .. } => Some(scalar),
118            _ => None,
119        }
120    }
121
122    pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {
123        self.scalar().map(|scalar| scalar.kind)
124    }
125
126    pub fn scalar_width(&self) -> Option<u8> {
128        self.scalar().map(|scalar| scalar.width)
129    }
130
131    pub fn scalar_for_conversions(
143        &self,
144        types: &crate::UniqueArena<crate::Type>,
145    ) -> Option<crate::Scalar> {
146        use crate::TypeInner as Ti;
147        match *self {
148            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
149                Some(scalar)
150            }
151            Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),
152            _ => None,
153        }
154    }
155
156    pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
157        match *self {
158            Self::Pointer { space, .. } => Some(space),
159            Self::ValuePointer { space, .. } => Some(space),
160            _ => None,
161        }
162    }
163
164    pub const fn pointer_base_type(&self) -> Option<TypeResolution> {
166        match *self {
167            crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),
168            crate::TypeInner::ValuePointer {
169                size: None, scalar, ..
170            } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),
171            crate::TypeInner::ValuePointer {
172                size: Some(size),
173                scalar,
174                ..
175            } => Some(TypeResolution::Value(crate::TypeInner::Vector {
176                size,
177                scalar,
178            })),
179            _ => None,
180        }
181    }
182
183    pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
184        match *self {
185            crate::TypeInner::Pointer { base, .. } => match types[base].inner {
186                crate::TypeInner::Atomic { .. } => true,
187                _ => false,
188            },
189            _ => false,
190        }
191    }
192
193    pub fn size(&self, gctx: super::GlobalCtx) -> u32 {
195        match *self {
196            Self::Scalar(scalar) | Self::Atomic(scalar) => scalar.width as u32,
197            Self::Vector { size, scalar } => size as u32 * scalar.width as u32,
198            Self::Matrix {
200                columns,
201                rows,
202                scalar,
203            } => super::Alignment::from(rows) * scalar.width as u32 * columns as u32,
204            Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN,
205            Self::Array {
206                base: _,
207                size,
208                stride,
209            } => {
210                let count = match size.resolve(gctx) {
211                    Ok(crate::proc::IndexableLength::Known(count)) => count,
212                    Err(_) => 0,
215                    Ok(crate::proc::IndexableLength::Dynamic) => 1,
217                };
218                count * stride
219            }
220            Self::Struct { span, .. } => span,
221            Self::Image { .. }
222            | Self::Sampler { .. }
223            | Self::AccelerationStructure { .. }
224            | Self::RayQuery { .. }
225            | Self::BindingArray { .. } => 0,
226        }
227    }
228
229    pub fn canonical_form(
238        &self,
239        types: &crate::UniqueArena<crate::Type>,
240    ) -> Option<crate::TypeInner> {
241        use crate::TypeInner as Ti;
242        match *self {
243            Ti::Pointer { base, space } => match types[base].inner {
244                Ti::Scalar(scalar) => Some(Ti::ValuePointer {
245                    size: None,
246                    scalar,
247                    space,
248                }),
249                Ti::Vector { size, scalar } => Some(Ti::ValuePointer {
250                    size: Some(size),
251                    scalar,
252                    space,
253                }),
254                _ => None,
255            },
256            _ => None,
257        }
258    }
259
260    pub fn non_struct_equivalent(
280        &self,
281        rhs: &ir::TypeInner,
282        types: &crate::UniqueArena<crate::Type>,
283    ) -> bool {
284        let left = self.canonical_form(types);
285        let right = rhs.canonical_form(types);
286
287        let left_struct = matches!(*self, ir::TypeInner::Struct { .. });
288        let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });
289
290        assert!(!left_struct || !right_struct);
291
292        left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
293    }
294
295    pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
296        use crate::TypeInner as Ti;
297        match *self {
298            Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
299            Ti::Struct { ref members, .. } => members
300                .last()
301                .map(|last| types[last.ty].inner.is_dynamically_sized(types))
302                .unwrap_or(false),
303            _ => false,
304        }
305    }
306
307    pub fn components(&self) -> Option<u32> {
308        Some(match *self {
309            Self::Vector { size, .. } => size as u32,
310            Self::Matrix { columns, .. } => columns as u32,
311            Self::Array {
312                size: crate::ArraySize::Constant(len),
313                ..
314            } => len.get(),
315            Self::Struct { ref members, .. } => members.len() as u32,
316            _ => return None,
317        })
318    }
319
320    pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
321        Some(match *self {
322            Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
323            Self::Matrix { rows, scalar, .. } => {
324                TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
325            }
326            Self::Array {
327                base,
328                size: crate::ArraySize::Constant(_),
329                ..
330            } => TypeResolution::Handle(base),
331            Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
332            _ => return None,
333        })
334    }
335
336    pub const fn vector_size_and_scalar(
339        &self,
340    ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
341        match *self {
342            crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
343            crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
344            crate::TypeInner::Matrix { .. }
345            | crate::TypeInner::Atomic(_)
346            | crate::TypeInner::Pointer { .. }
347            | crate::TypeInner::ValuePointer { .. }
348            | crate::TypeInner::Array { .. }
349            | crate::TypeInner::Struct { .. }
350            | crate::TypeInner::Image { .. }
351            | crate::TypeInner::Sampler { .. }
352            | crate::TypeInner::AccelerationStructure { .. }
353            | crate::TypeInner::RayQuery { .. }
354            | crate::TypeInner::BindingArray { .. } => None,
355        }
356    }
357
358    pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
363        match *self {
364            crate::TypeInner::Scalar(scalar)
365            | crate::TypeInner::Vector { scalar, .. }
366            | crate::TypeInner::Matrix { scalar, .. }
367            | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
368            crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
369            crate::TypeInner::ValuePointer { .. }
370            | crate::TypeInner::Pointer { .. }
371            | crate::TypeInner::Struct { .. }
372            | crate::TypeInner::Image { .. }
373            | crate::TypeInner::Sampler { .. }
374            | crate::TypeInner::AccelerationStructure { .. }
375            | crate::TypeInner::RayQuery { .. }
376            | crate::TypeInner::BindingArray { .. } => false,
377        }
378    }
379
380    pub fn automatically_converts_to(
409        &self,
410        goal: &Self,
411        types: &crate::UniqueArena<crate::Type>,
412    ) -> Option<(crate::Scalar, crate::Scalar)> {
413        use crate::ScalarKind as Sk;
414        use crate::TypeInner as Ti;
415
416        let expr_scalar;
422        let goal_scalar;
423        match (self, goal) {
424            (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
425                expr_scalar = expr;
426                goal_scalar = goal;
427            }
428            (
429                &Ti::Vector {
430                    size: expr_size,
431                    scalar: expr,
432                },
433                &Ti::Vector {
434                    size: goal_size,
435                    scalar: goal,
436                },
437            ) if expr_size == goal_size => {
438                expr_scalar = expr;
439                goal_scalar = goal;
440            }
441            (
442                &Ti::Matrix {
443                    rows: expr_rows,
444                    columns: expr_columns,
445                    scalar: expr,
446                },
447                &Ti::Matrix {
448                    rows: goal_rows,
449                    columns: goal_columns,
450                    scalar: goal,
451                },
452            ) if expr_rows == goal_rows && expr_columns == goal_columns => {
453                expr_scalar = expr;
454                goal_scalar = goal;
455            }
456            (
457                &Ti::Array {
458                    base: expr_base,
459                    size: expr_size,
460                    stride: _,
461                },
462                &Ti::Array {
463                    base: goal_base,
464                    size: goal_size,
465                    stride: _,
466                },
467            ) if expr_size == goal_size => {
468                return types[expr_base]
469                    .inner
470                    .automatically_converts_to(&types[goal_base].inner, types);
471            }
472            _ => return None,
473        }
474
475        match (expr_scalar.kind, goal_scalar.kind) {
476            (Sk::AbstractFloat, Sk::Float) => {}
477            (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
478            _ => return None,
479        }
480
481        log::trace!("      okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
482        Some((expr_scalar, goal_scalar))
483    }
484}
485
486pub trait IntFloatLimits<F>
489where
490    F: num_traits::Float,
491{
492    fn min_float() -> F;
495    fn max_float() -> F;
498}
499
500macro_rules! define_int_float_limits {
501    ($int:ty, $float:ty, $min:expr, $max:expr) => {
502        impl IntFloatLimits<$float> for $int {
503            fn min_float() -> $float {
504                $min
505            }
506            fn max_float() -> $float {
507                $max
508            }
509        }
510    };
511}
512
513define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
514define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
515define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
516define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
517define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
518define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
519define_int_float_limits!(
520    i64,
521    f32,
522    -9223372036854775808.0f32,
523    9223371487098961920.0f32
524);
525define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
526define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
527define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
528define_int_float_limits!(
529    i64,
530    f64,
531    -9223372036854775808.0f64,
532    9223372036854774784.0f64
533);
534define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
535
536pub fn min_max_float_representable_by(
541    float: crate::Scalar,
542    int: crate::Scalar,
543) -> (crate::Literal, crate::Literal) {
544    match (float, int) {
545        (crate::Scalar::F16, crate::Scalar::I32) => (
546            crate::Literal::F16(i32::min_float()),
547            crate::Literal::F16(i32::max_float()),
548        ),
549        (crate::Scalar::F16, crate::Scalar::U32) => (
550            crate::Literal::F16(u32::min_float()),
551            crate::Literal::F16(u32::max_float()),
552        ),
553        (crate::Scalar::F16, crate::Scalar::I64) => (
554            crate::Literal::F16(i64::min_float()),
555            crate::Literal::F16(i64::max_float()),
556        ),
557        (crate::Scalar::F16, crate::Scalar::U64) => (
558            crate::Literal::F16(u64::min_float()),
559            crate::Literal::F16(u64::max_float()),
560        ),
561        (crate::Scalar::F32, crate::Scalar::I32) => (
562            crate::Literal::F32(i32::min_float()),
563            crate::Literal::F32(i32::max_float()),
564        ),
565        (crate::Scalar::F32, crate::Scalar::U32) => (
566            crate::Literal::F32(u32::min_float()),
567            crate::Literal::F32(u32::max_float()),
568        ),
569        (crate::Scalar::F32, crate::Scalar::I64) => (
570            crate::Literal::F32(i64::min_float()),
571            crate::Literal::F32(i64::max_float()),
572        ),
573        (crate::Scalar::F32, crate::Scalar::U64) => (
574            crate::Literal::F32(u64::min_float()),
575            crate::Literal::F32(u64::max_float()),
576        ),
577        (crate::Scalar::F64, crate::Scalar::I32) => (
578            crate::Literal::F64(i32::min_float()),
579            crate::Literal::F64(i32::max_float()),
580        ),
581        (crate::Scalar::F64, crate::Scalar::U32) => (
582            crate::Literal::F64(u32::min_float()),
583            crate::Literal::F64(u32::max_float()),
584        ),
585        (crate::Scalar::F64, crate::Scalar::I64) => (
586            crate::Literal::F64(i64::min_float()),
587            crate::Literal::F64(i64::max_float()),
588        ),
589        (crate::Scalar::F64, crate::Scalar::U64) => (
590            crate::Literal::F64(u64::min_float()),
591            crate::Literal::F64(u64::max_float()),
592        ),
593        _ => unreachable!(),
594    }
595}