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 fn automatically_converts_to(
540 &self,
541 goal: &Self,
542 types: &crate::UniqueArena<crate::Type>,
543 ) -> Option<(crate::Scalar, crate::Scalar)> {
544 use crate::ScalarKind as Sk;
545 use crate::TypeInner as Ti;
546
547 let expr_scalar;
553 let goal_scalar;
554 match (self, goal) {
555 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
556 expr_scalar = expr;
557 goal_scalar = goal;
558 }
559 (
560 &Ti::Vector {
561 size: expr_size,
562 scalar: expr,
563 },
564 &Ti::Vector {
565 size: goal_size,
566 scalar: goal,
567 },
568 ) if expr_size == goal_size => {
569 expr_scalar = expr;
570 goal_scalar = goal;
571 }
572 (
573 &Ti::Matrix {
574 rows: expr_rows,
575 columns: expr_columns,
576 scalar: expr,
577 },
578 &Ti::Matrix {
579 rows: goal_rows,
580 columns: goal_columns,
581 scalar: goal,
582 },
583 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
584 expr_scalar = expr;
585 goal_scalar = goal;
586 }
587 (
588 &Ti::Array {
589 base: expr_base,
590 size: expr_size,
591 stride: _,
592 },
593 &Ti::Array {
594 base: goal_base,
595 size: goal_size,
596 stride: _,
597 },
598 ) if expr_size == goal_size => {
599 return types[expr_base]
600 .inner
601 .automatically_converts_to(&types[goal_base].inner, types);
602 }
603 _ => return None,
604 }
605
606 match (expr_scalar.kind, goal_scalar.kind) {
607 (Sk::AbstractFloat, Sk::Float) => {}
608 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
609 _ => return None,
610 }
611
612 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
613 Some((expr_scalar, goal_scalar))
614 }
615}
616
617pub trait IntFloatLimits<F>
620where
621 F: num_traits::Float,
622{
623 fn min_float() -> F;
626 fn max_float() -> F;
629}
630
631macro_rules! define_int_float_limits {
632 ($int:ty, $float:ty, $min:expr, $max:expr) => {
633 impl IntFloatLimits<$float> for $int {
634 fn min_float() -> $float {
635 $min
636 }
637 fn max_float() -> $float {
638 $max
639 }
640 }
641 };
642}
643
644define_int_float_limits!(i16, half::f16, half::f16::MIN, half::f16::MAX);
648define_int_float_limits!(u16, half::f16, half::f16::ZERO, half::f16::MAX);
649define_int_float_limits!(i16, f32, -32768.0f32, 32767.0f32);
650define_int_float_limits!(u16, f32, 0.0f32, 65535.0f32);
651define_int_float_limits!(i16, f64, -32768.0f64, 32767.0f64);
652define_int_float_limits!(u16, f64, 0.0f64, 65535.0f64);
653define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
654define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
655define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
656define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
657define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
658define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
659define_int_float_limits!(
660 i64,
661 f32,
662 -9223372036854775808.0f32,
663 9223371487098961920.0f32
664);
665define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
666define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
667define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
668define_int_float_limits!(
669 i64,
670 f64,
671 -9223372036854775808.0f64,
672 9223372036854774784.0f64
673);
674define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
675
676pub fn min_max_float_representable_by(
681 float: crate::Scalar,
682 int: crate::Scalar,
683) -> (crate::Literal, crate::Literal) {
684 match (float, int) {
685 (crate::Scalar::F16, crate::Scalar::I16) => (
686 crate::Literal::F16(i16::min_float()),
687 crate::Literal::F16(i16::max_float()),
688 ),
689 (crate::Scalar::F16, crate::Scalar::U16) => (
690 crate::Literal::F16(u16::min_float()),
691 crate::Literal::F16(u16::max_float()),
692 ),
693 (crate::Scalar::F16, crate::Scalar::I32) => (
694 crate::Literal::F16(i32::min_float()),
695 crate::Literal::F16(i32::max_float()),
696 ),
697 (crate::Scalar::F16, crate::Scalar::U32) => (
698 crate::Literal::F16(u32::min_float()),
699 crate::Literal::F16(u32::max_float()),
700 ),
701 (crate::Scalar::F16, crate::Scalar::I64) => (
702 crate::Literal::F16(i64::min_float()),
703 crate::Literal::F16(i64::max_float()),
704 ),
705 (crate::Scalar::F16, crate::Scalar::U64) => (
706 crate::Literal::F16(u64::min_float()),
707 crate::Literal::F16(u64::max_float()),
708 ),
709 (crate::Scalar::F32, crate::Scalar::I16) => (
710 crate::Literal::F32(i16::min_float()),
711 crate::Literal::F32(i16::max_float()),
712 ),
713 (crate::Scalar::F32, crate::Scalar::U16) => (
714 crate::Literal::F32(u16::min_float()),
715 crate::Literal::F32(u16::max_float()),
716 ),
717 (crate::Scalar::F32, crate::Scalar::I32) => (
718 crate::Literal::F32(i32::min_float()),
719 crate::Literal::F32(i32::max_float()),
720 ),
721 (crate::Scalar::F32, crate::Scalar::U32) => (
722 crate::Literal::F32(u32::min_float()),
723 crate::Literal::F32(u32::max_float()),
724 ),
725 (crate::Scalar::F32, crate::Scalar::I64) => (
726 crate::Literal::F32(i64::min_float()),
727 crate::Literal::F32(i64::max_float()),
728 ),
729 (crate::Scalar::F32, crate::Scalar::U64) => (
730 crate::Literal::F32(u64::min_float()),
731 crate::Literal::F32(u64::max_float()),
732 ),
733 (crate::Scalar::F64, crate::Scalar::I16) => (
734 crate::Literal::F64(i16::min_float()),
735 crate::Literal::F64(i16::max_float()),
736 ),
737 (crate::Scalar::F64, crate::Scalar::U16) => (
738 crate::Literal::F64(u16::min_float()),
739 crate::Literal::F64(u16::max_float()),
740 ),
741 (crate::Scalar::F64, crate::Scalar::I32) => (
742 crate::Literal::F64(i32::min_float()),
743 crate::Literal::F64(i32::max_float()),
744 ),
745 (crate::Scalar::F64, crate::Scalar::U32) => (
746 crate::Literal::F64(u32::min_float()),
747 crate::Literal::F64(u32::max_float()),
748 ),
749 (crate::Scalar::F64, crate::Scalar::I64) => (
750 crate::Literal::F64(i64::min_float()),
751 crate::Literal::F64(i64::max_float()),
752 ),
753 (crate::Scalar::F64, crate::Scalar::U64) => (
754 crate::Literal::F64(u64::min_float()),
755 crate::Literal::F64(u64::max_float()),
756 ),
757 _ => unreachable!(),
758 }
759}
760
761pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
763 match size {
764 crate::VectorSize::Bi => "2",
765 crate::VectorSize::Tri => "3",
766 crate::VectorSize::Quad => "4",
767 }
768}