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 _ => None,
145 }
146 }
147
148 pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {
149 self.scalar().map(|scalar| scalar.kind)
150 }
151
152 pub fn scalar_width(&self) -> Option<u8> {
154 self.scalar().map(|scalar| scalar.width)
155 }
156
157 pub fn scalar_for_conversions(
169 &self,
170 types: &crate::UniqueArena<crate::Type>,
171 ) -> Option<crate::Scalar> {
172 use crate::TypeInner as Ti;
173 match *self {
174 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
175 Some(scalar)
176 }
177 Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),
178 _ => None,
179 }
180 }
181
182 pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
183 match *self {
184 Self::Pointer { space, .. } => Some(space),
185 Self::ValuePointer { space, .. } => Some(space),
186 _ => None,
187 }
188 }
189
190 pub const fn pointer_base_type(&self) -> Option<TypeResolution> {
192 match *self {
193 crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),
194 crate::TypeInner::ValuePointer {
195 size: None, scalar, ..
196 } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),
197 crate::TypeInner::ValuePointer {
198 size: Some(size),
199 scalar,
200 ..
201 } => Some(TypeResolution::Value(crate::TypeInner::Vector {
202 size,
203 scalar,
204 })),
205 _ => None,
206 }
207 }
208
209 pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
210 match *self {
211 crate::TypeInner::Pointer { base, .. } => match types[base].inner {
212 crate::TypeInner::Atomic { .. } => true,
213 _ => false,
214 },
215 _ => false,
216 }
217 }
218
219 pub fn try_size(&self, gctx: super::GlobalCtx) -> Option<u32> {
222 match *self {
223 Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32),
224 Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32),
225 Self::Matrix {
227 columns,
228 rows,
229 scalar,
230 } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32),
231 Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN),
232 Self::Array {
233 base: _,
234 size,
235 stride,
236 } => {
237 let count = match size.resolve(gctx) {
238 Ok(crate::proc::IndexableLength::Known(count)) => count,
239 Err(_) => 0,
242 Ok(crate::proc::IndexableLength::Dynamic) => 1,
244 };
245 if count > MAX_TYPE_SIZE {
246 None
249 } else {
250 count
251 .checked_mul(stride)
252 .filter(|size| *size <= MAX_TYPE_SIZE)
253 }
254 }
255 Self::Struct { span, .. } => Some(span),
256 Self::Image { .. }
257 | Self::Sampler { .. }
258 | Self::AccelerationStructure { .. }
259 | Self::RayQuery { .. }
260 | Self::BindingArray { .. } => Some(0),
261 }
262 }
263
264 pub fn size(&self, gctx: super::GlobalCtx) -> u32 {
271 self.try_size(gctx).expect("type is too large")
272 }
273
274 pub fn canonical_form(
283 &self,
284 types: &crate::UniqueArena<crate::Type>,
285 ) -> Option<crate::TypeInner> {
286 use crate::TypeInner as Ti;
287 match *self {
288 Ti::Pointer { base, space } => match types[base].inner {
289 Ti::Scalar(scalar) => Some(Ti::ValuePointer {
290 size: None,
291 scalar,
292 space,
293 }),
294 Ti::Vector { size, scalar } => Some(Ti::ValuePointer {
295 size: Some(size),
296 scalar,
297 space,
298 }),
299 _ => None,
300 },
301 _ => None,
302 }
303 }
304
305 pub fn non_struct_equivalent(
325 &self,
326 rhs: &ir::TypeInner,
327 types: &crate::UniqueArena<crate::Type>,
328 ) -> bool {
329 let left = self.canonical_form(types);
330 let right = rhs.canonical_form(types);
331
332 let left_struct = matches!(*self, ir::TypeInner::Struct { .. });
333 let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });
334
335 assert!(!left_struct || !right_struct);
336
337 left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
338 }
339
340 pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
341 use crate::TypeInner as Ti;
342 match *self {
343 Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
344 Ti::Struct { ref members, .. } => members
345 .last()
346 .map(|last| types[last.ty].inner.is_dynamically_sized(types))
347 .unwrap_or(false),
348 _ => false,
349 }
350 }
351
352 pub fn components(&self) -> Option<u32> {
353 Some(match *self {
354 Self::Vector { size, .. } => size as u32,
355 Self::Matrix { columns, .. } => columns as u32,
356 Self::Array {
357 size: crate::ArraySize::Constant(len),
358 ..
359 } => len.get(),
360 Self::Struct { ref members, .. } => members.len() as u32,
361 _ => return None,
362 })
363 }
364
365 pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
366 Some(match *self {
367 Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
368 Self::Matrix { rows, scalar, .. } => {
369 TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
370 }
371 Self::Array {
372 base,
373 size: crate::ArraySize::Constant(_),
374 ..
375 } => TypeResolution::Handle(base),
376 Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
377 _ => return None,
378 })
379 }
380
381 pub const fn vector_size_and_scalar(
384 &self,
385 ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
386 match *self {
387 crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
388 crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
389 crate::TypeInner::Matrix { .. }
390 | crate::TypeInner::Atomic(_)
391 | crate::TypeInner::Pointer { .. }
392 | crate::TypeInner::ValuePointer { .. }
393 | crate::TypeInner::Array { .. }
394 | crate::TypeInner::Struct { .. }
395 | crate::TypeInner::Image { .. }
396 | crate::TypeInner::Sampler { .. }
397 | crate::TypeInner::AccelerationStructure { .. }
398 | crate::TypeInner::RayQuery { .. }
399 | crate::TypeInner::BindingArray { .. } => None,
400 }
401 }
402
403 pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
408 match *self {
409 crate::TypeInner::Scalar(scalar)
410 | crate::TypeInner::Vector { scalar, .. }
411 | crate::TypeInner::Matrix { scalar, .. }
412 | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
413 crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
414 crate::TypeInner::ValuePointer { .. }
415 | crate::TypeInner::Pointer { .. }
416 | crate::TypeInner::Struct { .. }
417 | crate::TypeInner::Image { .. }
418 | crate::TypeInner::Sampler { .. }
419 | crate::TypeInner::AccelerationStructure { .. }
420 | crate::TypeInner::RayQuery { .. }
421 | crate::TypeInner::BindingArray { .. } => false,
422 }
423 }
424
425 pub fn automatically_converts_to(
454 &self,
455 goal: &Self,
456 types: &crate::UniqueArena<crate::Type>,
457 ) -> Option<(crate::Scalar, crate::Scalar)> {
458 use crate::ScalarKind as Sk;
459 use crate::TypeInner as Ti;
460
461 let expr_scalar;
467 let goal_scalar;
468 match (self, goal) {
469 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
470 expr_scalar = expr;
471 goal_scalar = goal;
472 }
473 (
474 &Ti::Vector {
475 size: expr_size,
476 scalar: expr,
477 },
478 &Ti::Vector {
479 size: goal_size,
480 scalar: goal,
481 },
482 ) if expr_size == goal_size => {
483 expr_scalar = expr;
484 goal_scalar = goal;
485 }
486 (
487 &Ti::Matrix {
488 rows: expr_rows,
489 columns: expr_columns,
490 scalar: expr,
491 },
492 &Ti::Matrix {
493 rows: goal_rows,
494 columns: goal_columns,
495 scalar: goal,
496 },
497 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
498 expr_scalar = expr;
499 goal_scalar = goal;
500 }
501 (
502 &Ti::Array {
503 base: expr_base,
504 size: expr_size,
505 stride: _,
506 },
507 &Ti::Array {
508 base: goal_base,
509 size: goal_size,
510 stride: _,
511 },
512 ) if expr_size == goal_size => {
513 return types[expr_base]
514 .inner
515 .automatically_converts_to(&types[goal_base].inner, types);
516 }
517 _ => return None,
518 }
519
520 match (expr_scalar.kind, goal_scalar.kind) {
521 (Sk::AbstractFloat, Sk::Float) => {}
522 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
523 _ => return None,
524 }
525
526 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
527 Some((expr_scalar, goal_scalar))
528 }
529}
530
531pub trait IntFloatLimits<F>
534where
535 F: num_traits::Float,
536{
537 fn min_float() -> F;
540 fn max_float() -> F;
543}
544
545macro_rules! define_int_float_limits {
546 ($int:ty, $float:ty, $min:expr, $max:expr) => {
547 impl IntFloatLimits<$float> for $int {
548 fn min_float() -> $float {
549 $min
550 }
551 fn max_float() -> $float {
552 $max
553 }
554 }
555 };
556}
557
558define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
559define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
560define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
561define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
562define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
563define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
564define_int_float_limits!(
565 i64,
566 f32,
567 -9223372036854775808.0f32,
568 9223371487098961920.0f32
569);
570define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
571define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
572define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
573define_int_float_limits!(
574 i64,
575 f64,
576 -9223372036854775808.0f64,
577 9223372036854774784.0f64
578);
579define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
580
581pub fn min_max_float_representable_by(
586 float: crate::Scalar,
587 int: crate::Scalar,
588) -> (crate::Literal, crate::Literal) {
589 match (float, int) {
590 (crate::Scalar::F16, crate::Scalar::I32) => (
591 crate::Literal::F16(i32::min_float()),
592 crate::Literal::F16(i32::max_float()),
593 ),
594 (crate::Scalar::F16, crate::Scalar::U32) => (
595 crate::Literal::F16(u32::min_float()),
596 crate::Literal::F16(u32::max_float()),
597 ),
598 (crate::Scalar::F16, crate::Scalar::I64) => (
599 crate::Literal::F16(i64::min_float()),
600 crate::Literal::F16(i64::max_float()),
601 ),
602 (crate::Scalar::F16, crate::Scalar::U64) => (
603 crate::Literal::F16(u64::min_float()),
604 crate::Literal::F16(u64::max_float()),
605 ),
606 (crate::Scalar::F32, crate::Scalar::I32) => (
607 crate::Literal::F32(i32::min_float()),
608 crate::Literal::F32(i32::max_float()),
609 ),
610 (crate::Scalar::F32, crate::Scalar::U32) => (
611 crate::Literal::F32(u32::min_float()),
612 crate::Literal::F32(u32::max_float()),
613 ),
614 (crate::Scalar::F32, crate::Scalar::I64) => (
615 crate::Literal::F32(i64::min_float()),
616 crate::Literal::F32(i64::max_float()),
617 ),
618 (crate::Scalar::F32, crate::Scalar::U64) => (
619 crate::Literal::F32(u64::min_float()),
620 crate::Literal::F32(u64::max_float()),
621 ),
622 (crate::Scalar::F64, crate::Scalar::I32) => (
623 crate::Literal::F64(i32::min_float()),
624 crate::Literal::F64(i32::max_float()),
625 ),
626 (crate::Scalar::F64, crate::Scalar::U32) => (
627 crate::Literal::F64(u32::min_float()),
628 crate::Literal::F64(u32::max_float()),
629 ),
630 (crate::Scalar::F64, crate::Scalar::I64) => (
631 crate::Literal::F64(i64::min_float()),
632 crate::Literal::F64(i64::max_float()),
633 ),
634 (crate::Scalar::F64, crate::Scalar::U64) => (
635 crate::Literal::F64(u64::min_float()),
636 crate::Literal::F64(u64::max_float()),
637 ),
638 _ => unreachable!(),
639 }
640}
641
642pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
644 match size {
645 crate::VectorSize::Bi => "2",
646 crate::VectorSize::Tri => "3",
647 crate::VectorSize::Quad => "4",
648 }
649}