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 I32: Self = Self {
27 kind: crate::ScalarKind::Sint,
28 width: 4,
29 };
30 pub const U32: Self = Self {
31 kind: crate::ScalarKind::Uint,
32 width: 4,
33 };
34 pub const F16: Self = Self {
35 kind: crate::ScalarKind::Float,
36 width: 2,
37 };
38 pub const F32: Self = Self {
39 kind: crate::ScalarKind::Float,
40 width: 4,
41 };
42 pub const F64: Self = Self {
43 kind: crate::ScalarKind::Float,
44 width: 8,
45 };
46 pub const I64: Self = Self {
47 kind: crate::ScalarKind::Sint,
48 width: 8,
49 };
50 pub const U64: Self = Self {
51 kind: crate::ScalarKind::Uint,
52 width: 8,
53 };
54 pub const BOOL: Self = Self {
55 kind: crate::ScalarKind::Bool,
56 width: crate::BOOL_WIDTH,
57 };
58 pub const ABSTRACT_INT: Self = Self {
59 kind: crate::ScalarKind::AbstractInt,
60 width: crate::ABSTRACT_WIDTH,
61 };
62 pub const ABSTRACT_FLOAT: Self = Self {
63 kind: crate::ScalarKind::AbstractFloat,
64 width: crate::ABSTRACT_WIDTH,
65 };
66
67 pub const fn is_abstract(self) -> bool {
68 match self.kind {
69 crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true,
70 crate::ScalarKind::Sint
71 | crate::ScalarKind::Uint
72 | crate::ScalarKind::Float
73 | crate::ScalarKind::Bool => false,
74 }
75 }
76
77 pub const fn float(width: crate::Bytes) -> Self {
82 Self {
83 kind: crate::ScalarKind::Float,
84 width,
85 }
86 }
87
88 pub const fn to_inner_scalar(self) -> crate::TypeInner {
89 crate::TypeInner::Scalar(self)
90 }
91
92 pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner {
93 crate::TypeInner::Vector { size, scalar: self }
94 }
95
96 pub const fn to_inner_atomic(self) -> crate::TypeInner {
97 crate::TypeInner::Atomic(self)
98 }
99}
100
101pub fn concrete_int_scalars() -> impl Iterator<Item = ir::Scalar> {
106 [
107 ir::Scalar::I32,
108 ir::Scalar::U32,
109 ir::Scalar::I64,
110 ir::Scalar::U64,
111 ]
112 .into_iter()
113}
114
115pub fn vector_sizes() -> impl Iterator<Item = ir::VectorSize> + Clone {
117 static SIZES: [ir::VectorSize; 3] = [
118 ir::VectorSize::Bi,
119 ir::VectorSize::Tri,
120 ir::VectorSize::Quad,
121 ];
122
123 SIZES.iter().cloned()
124}
125
126const POINTER_SPAN: u32 = 4;
127
128impl crate::TypeInner {
129 pub const fn scalar(&self) -> Option<crate::Scalar> {
140 use crate::TypeInner as Ti;
141 match *self {
142 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar),
143 Ti::Matrix { scalar, .. } => Some(scalar),
144 Ti::CooperativeMatrix { scalar, .. } => Some(scalar),
145 _ => None,
146 }
147 }
148
149 pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {
150 self.scalar().map(|scalar| scalar.kind)
151 }
152
153 pub fn scalar_width(&self) -> Option<u8> {
155 self.scalar().map(|scalar| scalar.width)
156 }
157
158 pub fn scalar_for_conversions(
170 &self,
171 types: &crate::UniqueArena<crate::Type>,
172 ) -> Option<crate::Scalar> {
173 use crate::TypeInner as Ti;
174 match *self {
175 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
176 Some(scalar)
177 }
178 Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),
179 _ => None,
180 }
181 }
182
183 pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
184 match *self {
185 Self::Pointer { space, .. } => Some(space),
186 Self::ValuePointer { space, .. } => Some(space),
187 _ => None,
188 }
189 }
190
191 pub const fn pointer_base_type(&self) -> Option<TypeResolution> {
193 match *self {
194 crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),
195 crate::TypeInner::ValuePointer {
196 size: None, scalar, ..
197 } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),
198 crate::TypeInner::ValuePointer {
199 size: Some(size),
200 scalar,
201 ..
202 } => Some(TypeResolution::Value(crate::TypeInner::Vector {
203 size,
204 scalar,
205 })),
206 _ => None,
207 }
208 }
209
210 pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
211 match *self {
212 Self::Pointer { base, .. } => match types[base].inner {
213 Self::Atomic { .. } => true,
214 _ => false,
215 },
216 _ => false,
217 }
218 }
219
220 pub fn try_size(&self, gctx: super::GlobalCtx) -> Option<u32> {
223 match *self {
224 Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32),
225 Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32),
226 Self::Matrix {
228 columns,
229 rows,
230 scalar,
231 } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32),
232 Self::CooperativeMatrix {
233 columns,
234 rows,
235 scalar,
236 role: _,
237 } => Some(columns as u32 * rows as u32 * scalar.width as u32),
238 Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN),
239 Self::Array {
240 base: _,
241 size,
242 stride,
243 } => {
244 let count = match size.resolve(gctx) {
245 Ok(crate::proc::IndexableLength::Known(count)) => count,
246 Err(_) => 0,
249 Ok(crate::proc::IndexableLength::Dynamic) => 1,
251 };
252 if count > MAX_TYPE_SIZE {
253 None
256 } else {
257 count
258 .checked_mul(stride)
259 .filter(|size| *size <= MAX_TYPE_SIZE)
260 }
261 }
262 Self::Struct { span, .. } => Some(span),
263 Self::Image { .. }
264 | Self::Sampler { .. }
265 | Self::AccelerationStructure { .. }
266 | Self::RayQuery { .. }
267 | Self::BindingArray { .. } => Some(0),
268 }
269 }
270
271 pub fn size(&self, gctx: super::GlobalCtx) -> u32 {
278 self.try_size(gctx).expect("type is too large")
279 }
280
281 pub fn canonical_form(
290 &self,
291 types: &crate::UniqueArena<crate::Type>,
292 ) -> Option<crate::TypeInner> {
293 use crate::TypeInner as Ti;
294 match *self {
295 Ti::Pointer { base, space } => match types[base].inner {
296 Ti::Scalar(scalar) => Some(Ti::ValuePointer {
297 size: None,
298 scalar,
299 space,
300 }),
301 Ti::Vector { size, scalar } => Some(Ti::ValuePointer {
302 size: Some(size),
303 scalar,
304 space,
305 }),
306 _ => None,
307 },
308 _ => None,
309 }
310 }
311
312 pub fn non_struct_equivalent(
332 &self,
333 rhs: &ir::TypeInner,
334 types: &crate::UniqueArena<crate::Type>,
335 ) -> bool {
336 let left = self.canonical_form(types);
337 let right = rhs.canonical_form(types);
338
339 let left_struct = matches!(*self, ir::TypeInner::Struct { .. });
340 let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });
341
342 assert!(!left_struct || !right_struct);
343
344 left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
345 }
346
347 pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
349 use crate::TypeInner as Ti;
350 match *self {
351 Ti::Array {
352 size: crate::ArraySize::Constant(_),
353 ..
354 } => false,
355 Ti::Array {
356 size: crate::ArraySize::Pending(_) | crate::ArraySize::Dynamic,
357 ..
358 } => true,
359 Ti::Struct { ref members, .. } => members
360 .last()
361 .map(|last| types[last.ty].inner.is_dynamically_sized(types))
362 .unwrap_or(false),
363 _ => false,
364 }
365 }
366
367 pub fn is_constructible(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
369 use crate::TypeInner as Ti;
370 match *self {
371 Ti::Array { base, size, .. } => {
372 let fixed_size = match size {
373 ir::ArraySize::Constant(_) => true,
374 ir::ArraySize::Pending(_) | ir::ArraySize::Dynamic => false,
375 };
376 fixed_size && types[base].inner.is_constructible(types)
377 }
378 Ti::Struct { ref members, .. } => members
379 .iter()
380 .all(|member| types[member.ty].inner.is_constructible(types)),
381 Ti::Atomic(_)
382 | Ti::Pointer { .. }
383 | Ti::ValuePointer { .. }
384 | Ti::Image { .. }
385 | Ti::Sampler { .. }
386 | Ti::AccelerationStructure { .. }
387 | Ti::BindingArray { .. } => false,
388 Ti::Scalar(_)
389 | Ti::Vector { .. }
390 | Ti::Matrix { .. }
391 | Ti::RayQuery { .. }
392 | Ti::CooperativeMatrix { .. } => true,
393 }
394 }
395
396 pub const fn components(&self) -> Option<u32> {
397 Some(match *self {
398 Self::Vector { size, .. } => size as u32,
399 Self::Matrix { columns, .. } => columns as u32,
400 Self::Array {
401 size: crate::ArraySize::Constant(len),
402 ..
403 } => len.get(),
404 Self::Struct { ref members, .. } => members.len() as u32,
405 _ => return None,
406 })
407 }
408
409 pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
410 Some(match *self {
411 Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
412 Self::Matrix { rows, scalar, .. } => {
413 TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
414 }
415 Self::Array {
416 base,
417 size: crate::ArraySize::Constant(_),
418 ..
419 } => TypeResolution::Handle(base),
420 Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
421 _ => return None,
422 })
423 }
424
425 pub const fn vector_size_and_scalar(
428 &self,
429 ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
430 match *self {
431 crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
432 crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
433 crate::TypeInner::Matrix { .. }
434 | crate::TypeInner::CooperativeMatrix { .. }
435 | crate::TypeInner::Atomic(_)
436 | crate::TypeInner::Pointer { .. }
437 | crate::TypeInner::ValuePointer { .. }
438 | crate::TypeInner::Array { .. }
439 | crate::TypeInner::Struct { .. }
440 | crate::TypeInner::Image { .. }
441 | crate::TypeInner::Sampler { .. }
442 | crate::TypeInner::AccelerationStructure { .. }
443 | crate::TypeInner::RayQuery { .. }
444 | crate::TypeInner::BindingArray { .. } => None,
445 }
446 }
447
448 pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
453 match *self {
454 crate::TypeInner::Scalar(scalar)
455 | crate::TypeInner::Vector { scalar, .. }
456 | crate::TypeInner::Matrix { scalar, .. }
457 | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
458 crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
459 crate::TypeInner::CooperativeMatrix { .. }
460 | crate::TypeInner::ValuePointer { .. }
461 | crate::TypeInner::Pointer { .. }
462 | crate::TypeInner::Struct { .. }
463 | crate::TypeInner::Image { .. }
464 | crate::TypeInner::Sampler { .. }
465 | crate::TypeInner::AccelerationStructure { .. }
466 | crate::TypeInner::RayQuery { .. }
467 | crate::TypeInner::BindingArray { .. } => false,
468 }
469 }
470
471 pub fn automatically_converts_to(
500 &self,
501 goal: &Self,
502 types: &crate::UniqueArena<crate::Type>,
503 ) -> Option<(crate::Scalar, crate::Scalar)> {
504 use crate::ScalarKind as Sk;
505 use crate::TypeInner as Ti;
506
507 let expr_scalar;
513 let goal_scalar;
514 match (self, goal) {
515 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
516 expr_scalar = expr;
517 goal_scalar = goal;
518 }
519 (
520 &Ti::Vector {
521 size: expr_size,
522 scalar: expr,
523 },
524 &Ti::Vector {
525 size: goal_size,
526 scalar: goal,
527 },
528 ) if expr_size == goal_size => {
529 expr_scalar = expr;
530 goal_scalar = goal;
531 }
532 (
533 &Ti::Matrix {
534 rows: expr_rows,
535 columns: expr_columns,
536 scalar: expr,
537 },
538 &Ti::Matrix {
539 rows: goal_rows,
540 columns: goal_columns,
541 scalar: goal,
542 },
543 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
544 expr_scalar = expr;
545 goal_scalar = goal;
546 }
547 (
548 &Ti::Array {
549 base: expr_base,
550 size: expr_size,
551 stride: _,
552 },
553 &Ti::Array {
554 base: goal_base,
555 size: goal_size,
556 stride: _,
557 },
558 ) if expr_size == goal_size => {
559 return types[expr_base]
560 .inner
561 .automatically_converts_to(&types[goal_base].inner, types);
562 }
563 _ => return None,
564 }
565
566 match (expr_scalar.kind, goal_scalar.kind) {
567 (Sk::AbstractFloat, Sk::Float) => {}
568 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
569 _ => return None,
570 }
571
572 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
573 Some((expr_scalar, goal_scalar))
574 }
575}
576
577pub trait IntFloatLimits<F>
580where
581 F: num_traits::Float,
582{
583 fn min_float() -> F;
586 fn max_float() -> F;
589}
590
591macro_rules! define_int_float_limits {
592 ($int:ty, $float:ty, $min:expr, $max:expr) => {
593 impl IntFloatLimits<$float> for $int {
594 fn min_float() -> $float {
595 $min
596 }
597 fn max_float() -> $float {
598 $max
599 }
600 }
601 };
602}
603
604define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
605define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
606define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
607define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
608define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
609define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
610define_int_float_limits!(
611 i64,
612 f32,
613 -9223372036854775808.0f32,
614 9223371487098961920.0f32
615);
616define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
617define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
618define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
619define_int_float_limits!(
620 i64,
621 f64,
622 -9223372036854775808.0f64,
623 9223372036854774784.0f64
624);
625define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
626
627pub fn min_max_float_representable_by(
632 float: crate::Scalar,
633 int: crate::Scalar,
634) -> (crate::Literal, crate::Literal) {
635 match (float, int) {
636 (crate::Scalar::F16, crate::Scalar::I32) => (
637 crate::Literal::F16(i32::min_float()),
638 crate::Literal::F16(i32::max_float()),
639 ),
640 (crate::Scalar::F16, crate::Scalar::U32) => (
641 crate::Literal::F16(u32::min_float()),
642 crate::Literal::F16(u32::max_float()),
643 ),
644 (crate::Scalar::F16, crate::Scalar::I64) => (
645 crate::Literal::F16(i64::min_float()),
646 crate::Literal::F16(i64::max_float()),
647 ),
648 (crate::Scalar::F16, crate::Scalar::U64) => (
649 crate::Literal::F16(u64::min_float()),
650 crate::Literal::F16(u64::max_float()),
651 ),
652 (crate::Scalar::F32, crate::Scalar::I32) => (
653 crate::Literal::F32(i32::min_float()),
654 crate::Literal::F32(i32::max_float()),
655 ),
656 (crate::Scalar::F32, crate::Scalar::U32) => (
657 crate::Literal::F32(u32::min_float()),
658 crate::Literal::F32(u32::max_float()),
659 ),
660 (crate::Scalar::F32, crate::Scalar::I64) => (
661 crate::Literal::F32(i64::min_float()),
662 crate::Literal::F32(i64::max_float()),
663 ),
664 (crate::Scalar::F32, crate::Scalar::U64) => (
665 crate::Literal::F32(u64::min_float()),
666 crate::Literal::F32(u64::max_float()),
667 ),
668 (crate::Scalar::F64, crate::Scalar::I32) => (
669 crate::Literal::F64(i32::min_float()),
670 crate::Literal::F64(i32::max_float()),
671 ),
672 (crate::Scalar::F64, crate::Scalar::U32) => (
673 crate::Literal::F64(u32::min_float()),
674 crate::Literal::F64(u32::max_float()),
675 ),
676 (crate::Scalar::F64, crate::Scalar::I64) => (
677 crate::Literal::F64(i64::min_float()),
678 crate::Literal::F64(i64::max_float()),
679 ),
680 (crate::Scalar::F64, crate::Scalar::U64) => (
681 crate::Literal::F64(u64::min_float()),
682 crate::Literal::F64(u64::max_float()),
683 ),
684 _ => unreachable!(),
685 }
686}
687
688pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
690 match size {
691 crate::VectorSize::Bi => "2",
692 crate::VectorSize::Tri => "3",
693 crate::VectorSize::Quad => "4",
694 }
695}