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 {
348 use crate::TypeInner as Ti;
349 match *self {
350 Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
351 Ti::Struct { ref members, .. } => members
352 .last()
353 .map(|last| types[last.ty].inner.is_dynamically_sized(types))
354 .unwrap_or(false),
355 _ => false,
356 }
357 }
358
359 pub fn components(&self) -> Option<u32> {
360 Some(match *self {
361 Self::Vector { size, .. } => size as u32,
362 Self::Matrix { columns, .. } => columns as u32,
363 Self::Array {
364 size: crate::ArraySize::Constant(len),
365 ..
366 } => len.get(),
367 Self::Struct { ref members, .. } => members.len() as u32,
368 _ => return None,
369 })
370 }
371
372 pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
373 Some(match *self {
374 Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
375 Self::Matrix { rows, scalar, .. } => {
376 TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
377 }
378 Self::Array {
379 base,
380 size: crate::ArraySize::Constant(_),
381 ..
382 } => TypeResolution::Handle(base),
383 Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
384 _ => return None,
385 })
386 }
387
388 pub const fn vector_size_and_scalar(
391 &self,
392 ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
393 match *self {
394 crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
395 crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
396 crate::TypeInner::Matrix { .. }
397 | crate::TypeInner::CooperativeMatrix { .. }
398 | crate::TypeInner::Atomic(_)
399 | crate::TypeInner::Pointer { .. }
400 | crate::TypeInner::ValuePointer { .. }
401 | crate::TypeInner::Array { .. }
402 | crate::TypeInner::Struct { .. }
403 | crate::TypeInner::Image { .. }
404 | crate::TypeInner::Sampler { .. }
405 | crate::TypeInner::AccelerationStructure { .. }
406 | crate::TypeInner::RayQuery { .. }
407 | crate::TypeInner::BindingArray { .. } => None,
408 }
409 }
410
411 pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
416 match *self {
417 crate::TypeInner::Scalar(scalar)
418 | crate::TypeInner::Vector { scalar, .. }
419 | crate::TypeInner::Matrix { scalar, .. }
420 | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
421 crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
422 crate::TypeInner::CooperativeMatrix { .. }
423 | crate::TypeInner::ValuePointer { .. }
424 | crate::TypeInner::Pointer { .. }
425 | crate::TypeInner::Struct { .. }
426 | crate::TypeInner::Image { .. }
427 | crate::TypeInner::Sampler { .. }
428 | crate::TypeInner::AccelerationStructure { .. }
429 | crate::TypeInner::RayQuery { .. }
430 | crate::TypeInner::BindingArray { .. } => false,
431 }
432 }
433
434 pub fn automatically_converts_to(
463 &self,
464 goal: &Self,
465 types: &crate::UniqueArena<crate::Type>,
466 ) -> Option<(crate::Scalar, crate::Scalar)> {
467 use crate::ScalarKind as Sk;
468 use crate::TypeInner as Ti;
469
470 let expr_scalar;
476 let goal_scalar;
477 match (self, goal) {
478 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
479 expr_scalar = expr;
480 goal_scalar = goal;
481 }
482 (
483 &Ti::Vector {
484 size: expr_size,
485 scalar: expr,
486 },
487 &Ti::Vector {
488 size: goal_size,
489 scalar: goal,
490 },
491 ) if expr_size == goal_size => {
492 expr_scalar = expr;
493 goal_scalar = goal;
494 }
495 (
496 &Ti::Matrix {
497 rows: expr_rows,
498 columns: expr_columns,
499 scalar: expr,
500 },
501 &Ti::Matrix {
502 rows: goal_rows,
503 columns: goal_columns,
504 scalar: goal,
505 },
506 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
507 expr_scalar = expr;
508 goal_scalar = goal;
509 }
510 (
511 &Ti::Array {
512 base: expr_base,
513 size: expr_size,
514 stride: _,
515 },
516 &Ti::Array {
517 base: goal_base,
518 size: goal_size,
519 stride: _,
520 },
521 ) if expr_size == goal_size => {
522 return types[expr_base]
523 .inner
524 .automatically_converts_to(&types[goal_base].inner, types);
525 }
526 _ => return None,
527 }
528
529 match (expr_scalar.kind, goal_scalar.kind) {
530 (Sk::AbstractFloat, Sk::Float) => {}
531 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
532 _ => return None,
533 }
534
535 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
536 Some((expr_scalar, goal_scalar))
537 }
538}
539
540pub trait IntFloatLimits<F>
543where
544 F: num_traits::Float,
545{
546 fn min_float() -> F;
549 fn max_float() -> F;
552}
553
554macro_rules! define_int_float_limits {
555 ($int:ty, $float:ty, $min:expr, $max:expr) => {
556 impl IntFloatLimits<$float> for $int {
557 fn min_float() -> $float {
558 $min
559 }
560 fn max_float() -> $float {
561 $max
562 }
563 }
564 };
565}
566
567define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
568define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
569define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
570define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
571define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
572define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
573define_int_float_limits!(
574 i64,
575 f32,
576 -9223372036854775808.0f32,
577 9223371487098961920.0f32
578);
579define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
580define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
581define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
582define_int_float_limits!(
583 i64,
584 f64,
585 -9223372036854775808.0f64,
586 9223372036854774784.0f64
587);
588define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
589
590pub fn min_max_float_representable_by(
595 float: crate::Scalar,
596 int: crate::Scalar,
597) -> (crate::Literal, crate::Literal) {
598 match (float, int) {
599 (crate::Scalar::F16, crate::Scalar::I32) => (
600 crate::Literal::F16(i32::min_float()),
601 crate::Literal::F16(i32::max_float()),
602 ),
603 (crate::Scalar::F16, crate::Scalar::U32) => (
604 crate::Literal::F16(u32::min_float()),
605 crate::Literal::F16(u32::max_float()),
606 ),
607 (crate::Scalar::F16, crate::Scalar::I64) => (
608 crate::Literal::F16(i64::min_float()),
609 crate::Literal::F16(i64::max_float()),
610 ),
611 (crate::Scalar::F16, crate::Scalar::U64) => (
612 crate::Literal::F16(u64::min_float()),
613 crate::Literal::F16(u64::max_float()),
614 ),
615 (crate::Scalar::F32, crate::Scalar::I32) => (
616 crate::Literal::F32(i32::min_float()),
617 crate::Literal::F32(i32::max_float()),
618 ),
619 (crate::Scalar::F32, crate::Scalar::U32) => (
620 crate::Literal::F32(u32::min_float()),
621 crate::Literal::F32(u32::max_float()),
622 ),
623 (crate::Scalar::F32, crate::Scalar::I64) => (
624 crate::Literal::F32(i64::min_float()),
625 crate::Literal::F32(i64::max_float()),
626 ),
627 (crate::Scalar::F32, crate::Scalar::U64) => (
628 crate::Literal::F32(u64::min_float()),
629 crate::Literal::F32(u64::max_float()),
630 ),
631 (crate::Scalar::F64, crate::Scalar::I32) => (
632 crate::Literal::F64(i32::min_float()),
633 crate::Literal::F64(i32::max_float()),
634 ),
635 (crate::Scalar::F64, crate::Scalar::U32) => (
636 crate::Literal::F64(u32::min_float()),
637 crate::Literal::F64(u32::max_float()),
638 ),
639 (crate::Scalar::F64, crate::Scalar::I64) => (
640 crate::Literal::F64(i64::min_float()),
641 crate::Literal::F64(i64::max_float()),
642 ),
643 (crate::Scalar::F64, crate::Scalar::U64) => (
644 crate::Literal::F64(u64::min_float()),
645 crate::Literal::F64(u64::max_float()),
646 ),
647 _ => unreachable!(),
648 }
649}
650
651pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
653 match size {
654 crate::VectorSize::Bi => "2",
655 crate::VectorSize::Tri => "3",
656 crate::VectorSize::Quad => "4",
657 }
658}