1use crate::{ir, valid::MAX_TYPE_SIZE};
9
10use super::TypeResolution;
11
12impl crate::ScalarKind {
13 pub const fn is_numeric(self) -> bool {
14 match self {
15 crate::ScalarKind::Sint
16 | crate::ScalarKind::Uint
17 | crate::ScalarKind::Float
18 | crate::ScalarKind::AbstractInt
19 | crate::ScalarKind::AbstractFloat => true,
20 crate::ScalarKind::Bool => false,
21 }
22 }
23}
24
25impl crate::Scalar {
26 pub const I16: Self = Self {
27 kind: crate::ScalarKind::Sint,
28 width: 2,
29 };
30 pub const U16: Self = Self {
31 kind: crate::ScalarKind::Uint,
32 width: 2,
33 };
34 pub const I32: Self = Self {
35 kind: crate::ScalarKind::Sint,
36 width: 4,
37 };
38 pub const U32: Self = Self {
39 kind: crate::ScalarKind::Uint,
40 width: 4,
41 };
42 pub const F16: Self = Self {
43 kind: crate::ScalarKind::Float,
44 width: 2,
45 };
46 pub const F32: Self = Self {
47 kind: crate::ScalarKind::Float,
48 width: 4,
49 };
50 pub const F64: Self = Self {
51 kind: crate::ScalarKind::Float,
52 width: 8,
53 };
54 pub const I64: Self = Self {
55 kind: crate::ScalarKind::Sint,
56 width: 8,
57 };
58 pub const U64: Self = Self {
59 kind: crate::ScalarKind::Uint,
60 width: 8,
61 };
62 pub const BOOL: Self = Self {
63 kind: crate::ScalarKind::Bool,
64 width: crate::BOOL_WIDTH,
65 };
66 pub const ABSTRACT_INT: Self = Self {
67 kind: crate::ScalarKind::AbstractInt,
68 width: crate::ABSTRACT_WIDTH,
69 };
70 pub const ABSTRACT_FLOAT: Self = Self {
71 kind: crate::ScalarKind::AbstractFloat,
72 width: crate::ABSTRACT_WIDTH,
73 };
74
75 pub const fn is_abstract(self) -> bool {
76 match self.kind {
77 crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true,
78 crate::ScalarKind::Sint
79 | crate::ScalarKind::Uint
80 | crate::ScalarKind::Float
81 | crate::ScalarKind::Bool => false,
82 }
83 }
84
85 pub const fn float(width: crate::Bytes) -> Self {
90 Self {
91 kind: crate::ScalarKind::Float,
92 width,
93 }
94 }
95
96 pub const fn to_inner_scalar(self) -> crate::TypeInner {
97 crate::TypeInner::Scalar(self)
98 }
99
100 pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner {
101 crate::TypeInner::Vector { size, scalar: self }
102 }
103
104 pub const fn to_inner_atomic(self) -> crate::TypeInner {
105 crate::TypeInner::Atomic(self)
106 }
107}
108
109pub fn concrete_int_scalars() -> impl Iterator<Item = ir::Scalar> {
114 [
115 ir::Scalar::I32,
116 ir::Scalar::U32,
117 ir::Scalar::I16,
118 ir::Scalar::U16,
119 ir::Scalar::I64,
120 ir::Scalar::U64,
121 ]
122 .into_iter()
123}
124
125pub fn vector_sizes() -> impl Iterator<Item = ir::VectorSize> + Clone {
127 static SIZES: [ir::VectorSize; 3] = [
128 ir::VectorSize::Bi,
129 ir::VectorSize::Tri,
130 ir::VectorSize::Quad,
131 ];
132
133 SIZES.iter().cloned()
134}
135
136const POINTER_SPAN: u32 = 4;
137
138impl crate::TypeInner {
139 pub const fn scalar(&self) -> Option<crate::Scalar> {
150 use crate::TypeInner as Ti;
151 match *self {
152 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar),
153 Ti::Matrix { scalar, .. } => Some(scalar),
154 Ti::CooperativeMatrix { scalar, .. } => Some(scalar),
155 _ => None,
156 }
157 }
158
159 pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {
160 self.scalar().map(|scalar| scalar.kind)
161 }
162
163 pub fn scalar_width(&self) -> Option<u8> {
165 self.scalar().map(|scalar| scalar.width)
166 }
167
168 pub fn scalar_for_conversions(
180 &self,
181 types: &crate::UniqueArena<crate::Type>,
182 ) -> Option<crate::Scalar> {
183 use crate::TypeInner as Ti;
184 match *self {
185 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
186 Some(scalar)
187 }
188 Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),
189 _ => None,
190 }
191 }
192
193 pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
194 match *self {
195 Self::Pointer { space, .. } => Some(space),
196 Self::ValuePointer { space, .. } => Some(space),
197 _ => None,
198 }
199 }
200
201 pub const fn pointer_base_type(&self) -> Option<TypeResolution> {
203 match *self {
204 crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),
205 crate::TypeInner::ValuePointer {
206 size: None, scalar, ..
207 } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),
208 crate::TypeInner::ValuePointer {
209 size: Some(size),
210 scalar,
211 ..
212 } => Some(TypeResolution::Value(crate::TypeInner::Vector {
213 size,
214 scalar,
215 })),
216 _ => None,
217 }
218 }
219
220 pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
221 match *self {
222 Self::Pointer { base, .. } => match types[base].inner {
223 Self::Atomic { .. } => true,
224 _ => false,
225 },
226 _ => false,
227 }
228 }
229
230 pub fn try_size(&self, gctx: super::GlobalCtx) -> Option<u32> {
233 match *self {
234 Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32),
235 Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32),
236 Self::Matrix {
238 columns,
239 rows,
240 scalar,
241 } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32),
242 Self::CooperativeMatrix {
243 columns,
244 rows,
245 scalar,
246 role: _,
247 } => Some(columns as u32 * rows as u32 * scalar.width as u32),
248 Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN),
249 Self::Array {
250 base: _,
251 size,
252 stride,
253 } => {
254 let count = match size.resolve(gctx) {
255 Ok(crate::proc::IndexableLength::Known(count)) => count,
256 Err(_) => 0,
259 Ok(crate::proc::IndexableLength::Dynamic) => 1,
261 };
262 if count > MAX_TYPE_SIZE {
263 None
266 } else {
267 count
268 .checked_mul(stride)
269 .filter(|size| *size <= MAX_TYPE_SIZE)
270 }
271 }
272 Self::Struct { span, .. } => Some(span),
273 Self::Image { .. }
274 | Self::Sampler { .. }
275 | Self::AccelerationStructure { .. }
276 | Self::RayQuery { .. }
277 | Self::BindingArray { .. } => Some(0),
278 }
279 }
280
281 pub fn size(&self, gctx: super::GlobalCtx) -> u32 {
288 self.try_size(gctx).expect("type is too large")
289 }
290
291 pub fn canonical_form(
300 &self,
301 types: &crate::UniqueArena<crate::Type>,
302 ) -> Option<crate::TypeInner> {
303 use crate::TypeInner as Ti;
304 match *self {
305 Ti::Pointer { base, space } => match types[base].inner {
306 Ti::Scalar(scalar) => Some(Ti::ValuePointer {
307 size: None,
308 scalar,
309 space,
310 }),
311 Ti::Vector { size, scalar } => Some(Ti::ValuePointer {
312 size: Some(size),
313 scalar,
314 space,
315 }),
316 _ => None,
317 },
318 _ => None,
319 }
320 }
321
322 pub fn non_struct_equivalent(
342 &self,
343 rhs: &ir::TypeInner,
344 types: &crate::UniqueArena<crate::Type>,
345 ) -> bool {
346 let left = self.canonical_form(types);
347 let right = rhs.canonical_form(types);
348
349 let left_struct = matches!(*self, ir::TypeInner::Struct { .. });
350 let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });
351
352 assert!(!left_struct || !right_struct);
353
354 left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
355 }
356
357 pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
359 use crate::TypeInner as Ti;
360 match *self {
361 Ti::Array {
362 size: crate::ArraySize::Constant(_),
363 ..
364 } => false,
365 Ti::Array {
366 size: crate::ArraySize::Pending(_) | crate::ArraySize::Dynamic,
367 ..
368 } => true,
369 Ti::Struct { ref members, .. } => members
370 .last()
371 .map(|last| types[last.ty].inner.is_dynamically_sized(types))
372 .unwrap_or(false),
373 _ => false,
374 }
375 }
376
377 pub fn needs_host_buffer_byte_size(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
387 use crate::TypeInner as Ti;
388 match *self {
389 Ti::Struct { ref members, .. } => members.last().is_some_and(|m| {
390 matches!(
391 types[m.ty].inner,
392 Ti::Array {
393 size: crate::ArraySize::Dynamic,
394 ..
395 }
396 )
397 }),
398 Ti::Array {
399 size: crate::ArraySize::Dynamic,
400 ..
401 } => true,
402 Ti::BindingArray { base, .. } => types[base].inner.needs_host_buffer_byte_size(types),
403 _ => false,
404 }
405 }
406
407 pub fn is_constructible(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
409 use crate::TypeInner as Ti;
410 match *self {
411 Ti::Array { base, size, .. } => {
412 let fixed_size = match size {
413 ir::ArraySize::Constant(_) => true,
414 ir::ArraySize::Pending(_) | ir::ArraySize::Dynamic => false,
415 };
416 fixed_size && types[base].inner.is_constructible(types)
417 }
418 Ti::Struct { ref members, .. } => members
419 .iter()
420 .all(|member| types[member.ty].inner.is_constructible(types)),
421 Ti::Atomic(_)
422 | Ti::Pointer { .. }
423 | Ti::ValuePointer { .. }
424 | Ti::Image { .. }
425 | Ti::Sampler { .. }
426 | Ti::AccelerationStructure { .. }
427 | Ti::BindingArray { .. } => false,
428 Ti::Scalar(_)
429 | Ti::Vector { .. }
430 | Ti::Matrix { .. }
431 | Ti::RayQuery { .. }
432 | Ti::CooperativeMatrix { .. } => true,
433 }
434 }
435
436 pub const fn components(&self) -> Option<u32> {
437 Some(match *self {
438 Self::Vector { size, .. } => size as u32,
439 Self::Matrix { columns, .. } => columns as u32,
440 Self::Array {
441 size: crate::ArraySize::Constant(len),
442 ..
443 } => len.get(),
444 Self::Struct { ref members, .. } => members.len() as u32,
445 _ => return None,
446 })
447 }
448
449 pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
450 Some(match *self {
451 Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
452 Self::Matrix { rows, scalar, .. } => {
453 TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
454 }
455 Self::Array {
456 base,
457 size: crate::ArraySize::Constant(_),
458 ..
459 } => TypeResolution::Handle(base),
460 Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
461 _ => return None,
462 })
463 }
464
465 pub const fn vector_size_and_scalar(
468 &self,
469 ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
470 match *self {
471 crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
472 crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
473 crate::TypeInner::Matrix { .. }
474 | crate::TypeInner::CooperativeMatrix { .. }
475 | crate::TypeInner::Atomic(_)
476 | crate::TypeInner::Pointer { .. }
477 | crate::TypeInner::ValuePointer { .. }
478 | crate::TypeInner::Array { .. }
479 | crate::TypeInner::Struct { .. }
480 | crate::TypeInner::Image { .. }
481 | crate::TypeInner::Sampler { .. }
482 | crate::TypeInner::AccelerationStructure { .. }
483 | crate::TypeInner::RayQuery { .. }
484 | crate::TypeInner::BindingArray { .. } => None,
485 }
486 }
487
488 pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
493 match *self {
494 crate::TypeInner::Scalar(scalar)
495 | crate::TypeInner::Vector { scalar, .. }
496 | crate::TypeInner::Matrix { scalar, .. }
497 | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
498 crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
499 crate::TypeInner::CooperativeMatrix { .. }
500 | crate::TypeInner::ValuePointer { .. }
501 | crate::TypeInner::Pointer { .. }
502 | crate::TypeInner::Struct { .. }
503 | crate::TypeInner::Image { .. }
504 | crate::TypeInner::Sampler { .. }
505 | crate::TypeInner::AccelerationStructure { .. }
506 | crate::TypeInner::RayQuery { .. }
507 | crate::TypeInner::BindingArray { .. } => false,
508 }
509 }
510
511 pub const fn is_external_image(&self) -> bool {
513 matches!(
514 self,
515 crate::TypeInner::Image {
516 class: crate::ImageClass::External,
517 ..
518 }
519 )
520 }
521
522 pub fn automatically_converts_to(
551 &self,
552 goal: &Self,
553 types: &crate::UniqueArena<crate::Type>,
554 ) -> Option<(crate::Scalar, crate::Scalar)> {
555 use crate::ScalarKind as Sk;
556 use crate::TypeInner as Ti;
557
558 let expr_scalar;
564 let goal_scalar;
565 match (self, goal) {
566 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
567 expr_scalar = expr;
568 goal_scalar = goal;
569 }
570 (
571 &Ti::Vector {
572 size: expr_size,
573 scalar: expr,
574 },
575 &Ti::Vector {
576 size: goal_size,
577 scalar: goal,
578 },
579 ) if expr_size == goal_size => {
580 expr_scalar = expr;
581 goal_scalar = goal;
582 }
583 (
584 &Ti::Matrix {
585 rows: expr_rows,
586 columns: expr_columns,
587 scalar: expr,
588 },
589 &Ti::Matrix {
590 rows: goal_rows,
591 columns: goal_columns,
592 scalar: goal,
593 },
594 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
595 expr_scalar = expr;
596 goal_scalar = goal;
597 }
598 (
599 &Ti::Array {
600 base: expr_base,
601 size: expr_size,
602 stride: _,
603 },
604 &Ti::Array {
605 base: goal_base,
606 size: goal_size,
607 stride: _,
608 },
609 ) if expr_size == goal_size => {
610 return types[expr_base]
611 .inner
612 .automatically_converts_to(&types[goal_base].inner, types);
613 }
614 _ => return None,
615 }
616
617 match (expr_scalar.kind, goal_scalar.kind) {
618 (Sk::AbstractFloat, Sk::Float) => {}
619 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
620 _ => return None,
621 }
622
623 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
624 Some((expr_scalar, goal_scalar))
625 }
626}
627
628pub trait IntFloatLimits<F>
631where
632 F: num_traits::Float,
633{
634 fn min_float() -> F;
637 fn max_float() -> F;
640}
641
642macro_rules! define_int_float_limits {
643 ($int:ty, $float:ty, $min:expr, $max:expr) => {
644 impl IntFloatLimits<$float> for $int {
645 fn min_float() -> $float {
646 $min
647 }
648 fn max_float() -> $float {
649 $max
650 }
651 }
652 };
653}
654
655define_int_float_limits!(i16, half::f16, half::f16::MIN, half::f16::MAX);
659define_int_float_limits!(u16, half::f16, half::f16::ZERO, half::f16::MAX);
660define_int_float_limits!(i16, f32, -32768.0f32, 32767.0f32);
661define_int_float_limits!(u16, f32, 0.0f32, 65535.0f32);
662define_int_float_limits!(i16, f64, -32768.0f64, 32767.0f64);
663define_int_float_limits!(u16, f64, 0.0f64, 65535.0f64);
664define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
665define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
666define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
667define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
668define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
669define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
670define_int_float_limits!(
671 i64,
672 f32,
673 -9223372036854775808.0f32,
674 9223371487098961920.0f32
675);
676define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
677define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
678define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
679define_int_float_limits!(
680 i64,
681 f64,
682 -9223372036854775808.0f64,
683 9223372036854774784.0f64
684);
685define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
686
687pub fn min_max_float_representable_by(
692 float: crate::Scalar,
693 int: crate::Scalar,
694) -> (crate::Literal, crate::Literal) {
695 match (float, int) {
696 (crate::Scalar::F16, crate::Scalar::I16) => (
697 crate::Literal::F16(i16::min_float()),
698 crate::Literal::F16(i16::max_float()),
699 ),
700 (crate::Scalar::F16, crate::Scalar::U16) => (
701 crate::Literal::F16(u16::min_float()),
702 crate::Literal::F16(u16::max_float()),
703 ),
704 (crate::Scalar::F16, crate::Scalar::I32) => (
705 crate::Literal::F16(i32::min_float()),
706 crate::Literal::F16(i32::max_float()),
707 ),
708 (crate::Scalar::F16, crate::Scalar::U32) => (
709 crate::Literal::F16(u32::min_float()),
710 crate::Literal::F16(u32::max_float()),
711 ),
712 (crate::Scalar::F16, crate::Scalar::I64) => (
713 crate::Literal::F16(i64::min_float()),
714 crate::Literal::F16(i64::max_float()),
715 ),
716 (crate::Scalar::F16, crate::Scalar::U64) => (
717 crate::Literal::F16(u64::min_float()),
718 crate::Literal::F16(u64::max_float()),
719 ),
720 (crate::Scalar::F32, crate::Scalar::I16) => (
721 crate::Literal::F32(i16::min_float()),
722 crate::Literal::F32(i16::max_float()),
723 ),
724 (crate::Scalar::F32, crate::Scalar::U16) => (
725 crate::Literal::F32(u16::min_float()),
726 crate::Literal::F32(u16::max_float()),
727 ),
728 (crate::Scalar::F32, crate::Scalar::I32) => (
729 crate::Literal::F32(i32::min_float()),
730 crate::Literal::F32(i32::max_float()),
731 ),
732 (crate::Scalar::F32, crate::Scalar::U32) => (
733 crate::Literal::F32(u32::min_float()),
734 crate::Literal::F32(u32::max_float()),
735 ),
736 (crate::Scalar::F32, crate::Scalar::I64) => (
737 crate::Literal::F32(i64::min_float()),
738 crate::Literal::F32(i64::max_float()),
739 ),
740 (crate::Scalar::F32, crate::Scalar::U64) => (
741 crate::Literal::F32(u64::min_float()),
742 crate::Literal::F32(u64::max_float()),
743 ),
744 (crate::Scalar::F64, crate::Scalar::I16) => (
745 crate::Literal::F64(i16::min_float()),
746 crate::Literal::F64(i16::max_float()),
747 ),
748 (crate::Scalar::F64, crate::Scalar::U16) => (
749 crate::Literal::F64(u16::min_float()),
750 crate::Literal::F64(u16::max_float()),
751 ),
752 (crate::Scalar::F64, crate::Scalar::I32) => (
753 crate::Literal::F64(i32::min_float()),
754 crate::Literal::F64(i32::max_float()),
755 ),
756 (crate::Scalar::F64, crate::Scalar::U32) => (
757 crate::Literal::F64(u32::min_float()),
758 crate::Literal::F64(u32::max_float()),
759 ),
760 (crate::Scalar::F64, crate::Scalar::I64) => (
761 crate::Literal::F64(i64::min_float()),
762 crate::Literal::F64(i64::max_float()),
763 ),
764 (crate::Scalar::F64, crate::Scalar::U64) => (
765 crate::Literal::F64(u64::min_float()),
766 crate::Literal::F64(u64::max_float()),
767 ),
768 _ => unreachable!(),
769 }
770}
771
772pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
774 match size {
775 crate::VectorSize::Bi => "2",
776 crate::VectorSize::Tri => "3",
777 crate::VectorSize::Quad => "4",
778 }
779}