1use crate::{ir, valid::MAX_TYPE_SIZE};
8
9use super::TypeResolution;
10
11impl crate::ScalarKind {
12 pub const fn is_numeric(self) -> bool {
13 match self {
14 crate::ScalarKind::Sint
15 | crate::ScalarKind::Uint
16 | crate::ScalarKind::Float
17 | crate::ScalarKind::AbstractInt
18 | crate::ScalarKind::AbstractFloat => true,
19 crate::ScalarKind::Bool => false,
20 }
21 }
22}
23
24impl crate::Scalar {
25 pub const I32: Self = Self {
26 kind: crate::ScalarKind::Sint,
27 width: 4,
28 };
29 pub const U32: Self = Self {
30 kind: crate::ScalarKind::Uint,
31 width: 4,
32 };
33 pub const F16: Self = Self {
34 kind: crate::ScalarKind::Float,
35 width: 2,
36 };
37 pub const F32: Self = Self {
38 kind: crate::ScalarKind::Float,
39 width: 4,
40 };
41 pub const F64: Self = Self {
42 kind: crate::ScalarKind::Float,
43 width: 8,
44 };
45 pub const I64: Self = Self {
46 kind: crate::ScalarKind::Sint,
47 width: 8,
48 };
49 pub const U64: Self = Self {
50 kind: crate::ScalarKind::Uint,
51 width: 8,
52 };
53 pub const BOOL: Self = Self {
54 kind: crate::ScalarKind::Bool,
55 width: crate::BOOL_WIDTH,
56 };
57 pub const ABSTRACT_INT: Self = Self {
58 kind: crate::ScalarKind::AbstractInt,
59 width: crate::ABSTRACT_WIDTH,
60 };
61 pub const ABSTRACT_FLOAT: Self = Self {
62 kind: crate::ScalarKind::AbstractFloat,
63 width: crate::ABSTRACT_WIDTH,
64 };
65
66 pub const fn is_abstract(self) -> bool {
67 match self.kind {
68 crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true,
69 crate::ScalarKind::Sint
70 | crate::ScalarKind::Uint
71 | crate::ScalarKind::Float
72 | crate::ScalarKind::Bool => false,
73 }
74 }
75
76 pub const fn float(width: crate::Bytes) -> Self {
81 Self {
82 kind: crate::ScalarKind::Float,
83 width,
84 }
85 }
86
87 pub const fn to_inner_scalar(self) -> crate::TypeInner {
88 crate::TypeInner::Scalar(self)
89 }
90
91 pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner {
92 crate::TypeInner::Vector { size, scalar: self }
93 }
94
95 pub const fn to_inner_atomic(self) -> crate::TypeInner {
96 crate::TypeInner::Atomic(self)
97 }
98}
99
100const POINTER_SPAN: u32 = 4;
101
102impl crate::TypeInner {
103 pub const fn scalar(&self) -> Option<crate::Scalar> {
114 use crate::TypeInner as Ti;
115 match *self {
116 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar),
117 Ti::Matrix { scalar, .. } => Some(scalar),
118 _ => None,
119 }
120 }
121
122 pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {
123 self.scalar().map(|scalar| scalar.kind)
124 }
125
126 pub fn scalar_width(&self) -> Option<u8> {
128 self.scalar().map(|scalar| scalar.width)
129 }
130
131 pub fn scalar_for_conversions(
143 &self,
144 types: &crate::UniqueArena<crate::Type>,
145 ) -> Option<crate::Scalar> {
146 use crate::TypeInner as Ti;
147 match *self {
148 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
149 Some(scalar)
150 }
151 Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),
152 _ => None,
153 }
154 }
155
156 pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
157 match *self {
158 Self::Pointer { space, .. } => Some(space),
159 Self::ValuePointer { space, .. } => Some(space),
160 _ => None,
161 }
162 }
163
164 pub const fn pointer_base_type(&self) -> Option<TypeResolution> {
166 match *self {
167 crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),
168 crate::TypeInner::ValuePointer {
169 size: None, scalar, ..
170 } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),
171 crate::TypeInner::ValuePointer {
172 size: Some(size),
173 scalar,
174 ..
175 } => Some(TypeResolution::Value(crate::TypeInner::Vector {
176 size,
177 scalar,
178 })),
179 _ => None,
180 }
181 }
182
183 pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
184 match *self {
185 crate::TypeInner::Pointer { base, .. } => match types[base].inner {
186 crate::TypeInner::Atomic { .. } => true,
187 _ => false,
188 },
189 _ => false,
190 }
191 }
192
193 pub fn try_size(&self, gctx: super::GlobalCtx) -> Option<u32> {
196 match *self {
197 Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32),
198 Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32),
199 Self::Matrix {
201 columns,
202 rows,
203 scalar,
204 } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32),
205 Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN),
206 Self::Array {
207 base: _,
208 size,
209 stride,
210 } => {
211 let count = match size.resolve(gctx) {
212 Ok(crate::proc::IndexableLength::Known(count)) => count,
213 Err(_) => 0,
216 Ok(crate::proc::IndexableLength::Dynamic) => 1,
218 };
219 if count > MAX_TYPE_SIZE {
220 None
223 } else {
224 count
225 .checked_mul(stride)
226 .filter(|size| *size <= MAX_TYPE_SIZE)
227 }
228 }
229 Self::Struct { span, .. } => Some(span),
230 Self::Image { .. }
231 | Self::Sampler { .. }
232 | Self::AccelerationStructure { .. }
233 | Self::RayQuery { .. }
234 | Self::BindingArray { .. } => Some(0),
235 }
236 }
237
238 pub fn size(&self, gctx: super::GlobalCtx) -> u32 {
245 self.try_size(gctx).expect("type is too large")
246 }
247
248 pub fn canonical_form(
257 &self,
258 types: &crate::UniqueArena<crate::Type>,
259 ) -> Option<crate::TypeInner> {
260 use crate::TypeInner as Ti;
261 match *self {
262 Ti::Pointer { base, space } => match types[base].inner {
263 Ti::Scalar(scalar) => Some(Ti::ValuePointer {
264 size: None,
265 scalar,
266 space,
267 }),
268 Ti::Vector { size, scalar } => Some(Ti::ValuePointer {
269 size: Some(size),
270 scalar,
271 space,
272 }),
273 _ => None,
274 },
275 _ => None,
276 }
277 }
278
279 pub fn non_struct_equivalent(
299 &self,
300 rhs: &ir::TypeInner,
301 types: &crate::UniqueArena<crate::Type>,
302 ) -> bool {
303 let left = self.canonical_form(types);
304 let right = rhs.canonical_form(types);
305
306 let left_struct = matches!(*self, ir::TypeInner::Struct { .. });
307 let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });
308
309 assert!(!left_struct || !right_struct);
310
311 left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
312 }
313
314 pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
315 use crate::TypeInner as Ti;
316 match *self {
317 Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
318 Ti::Struct { ref members, .. } => members
319 .last()
320 .map(|last| types[last.ty].inner.is_dynamically_sized(types))
321 .unwrap_or(false),
322 _ => false,
323 }
324 }
325
326 pub fn components(&self) -> Option<u32> {
327 Some(match *self {
328 Self::Vector { size, .. } => size as u32,
329 Self::Matrix { columns, .. } => columns as u32,
330 Self::Array {
331 size: crate::ArraySize::Constant(len),
332 ..
333 } => len.get(),
334 Self::Struct { ref members, .. } => members.len() as u32,
335 _ => return None,
336 })
337 }
338
339 pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
340 Some(match *self {
341 Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
342 Self::Matrix { rows, scalar, .. } => {
343 TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
344 }
345 Self::Array {
346 base,
347 size: crate::ArraySize::Constant(_),
348 ..
349 } => TypeResolution::Handle(base),
350 Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
351 _ => return None,
352 })
353 }
354
355 pub const fn vector_size_and_scalar(
358 &self,
359 ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
360 match *self {
361 crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
362 crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
363 crate::TypeInner::Matrix { .. }
364 | crate::TypeInner::Atomic(_)
365 | crate::TypeInner::Pointer { .. }
366 | crate::TypeInner::ValuePointer { .. }
367 | crate::TypeInner::Array { .. }
368 | crate::TypeInner::Struct { .. }
369 | crate::TypeInner::Image { .. }
370 | crate::TypeInner::Sampler { .. }
371 | crate::TypeInner::AccelerationStructure { .. }
372 | crate::TypeInner::RayQuery { .. }
373 | crate::TypeInner::BindingArray { .. } => None,
374 }
375 }
376
377 pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
382 match *self {
383 crate::TypeInner::Scalar(scalar)
384 | crate::TypeInner::Vector { scalar, .. }
385 | crate::TypeInner::Matrix { scalar, .. }
386 | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
387 crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
388 crate::TypeInner::ValuePointer { .. }
389 | crate::TypeInner::Pointer { .. }
390 | crate::TypeInner::Struct { .. }
391 | crate::TypeInner::Image { .. }
392 | crate::TypeInner::Sampler { .. }
393 | crate::TypeInner::AccelerationStructure { .. }
394 | crate::TypeInner::RayQuery { .. }
395 | crate::TypeInner::BindingArray { .. } => false,
396 }
397 }
398
399 pub fn automatically_converts_to(
428 &self,
429 goal: &Self,
430 types: &crate::UniqueArena<crate::Type>,
431 ) -> Option<(crate::Scalar, crate::Scalar)> {
432 use crate::ScalarKind as Sk;
433 use crate::TypeInner as Ti;
434
435 let expr_scalar;
441 let goal_scalar;
442 match (self, goal) {
443 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
444 expr_scalar = expr;
445 goal_scalar = goal;
446 }
447 (
448 &Ti::Vector {
449 size: expr_size,
450 scalar: expr,
451 },
452 &Ti::Vector {
453 size: goal_size,
454 scalar: goal,
455 },
456 ) if expr_size == goal_size => {
457 expr_scalar = expr;
458 goal_scalar = goal;
459 }
460 (
461 &Ti::Matrix {
462 rows: expr_rows,
463 columns: expr_columns,
464 scalar: expr,
465 },
466 &Ti::Matrix {
467 rows: goal_rows,
468 columns: goal_columns,
469 scalar: goal,
470 },
471 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
472 expr_scalar = expr;
473 goal_scalar = goal;
474 }
475 (
476 &Ti::Array {
477 base: expr_base,
478 size: expr_size,
479 stride: _,
480 },
481 &Ti::Array {
482 base: goal_base,
483 size: goal_size,
484 stride: _,
485 },
486 ) if expr_size == goal_size => {
487 return types[expr_base]
488 .inner
489 .automatically_converts_to(&types[goal_base].inner, types);
490 }
491 _ => return None,
492 }
493
494 match (expr_scalar.kind, goal_scalar.kind) {
495 (Sk::AbstractFloat, Sk::Float) => {}
496 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
497 _ => return None,
498 }
499
500 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
501 Some((expr_scalar, goal_scalar))
502 }
503}
504
505pub trait IntFloatLimits<F>
508where
509 F: num_traits::Float,
510{
511 fn min_float() -> F;
514 fn max_float() -> F;
517}
518
519macro_rules! define_int_float_limits {
520 ($int:ty, $float:ty, $min:expr, $max:expr) => {
521 impl IntFloatLimits<$float> for $int {
522 fn min_float() -> $float {
523 $min
524 }
525 fn max_float() -> $float {
526 $max
527 }
528 }
529 };
530}
531
532define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
533define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
534define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
535define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
536define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
537define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
538define_int_float_limits!(
539 i64,
540 f32,
541 -9223372036854775808.0f32,
542 9223371487098961920.0f32
543);
544define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
545define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
546define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
547define_int_float_limits!(
548 i64,
549 f64,
550 -9223372036854775808.0f64,
551 9223372036854774784.0f64
552);
553define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
554
555pub fn min_max_float_representable_by(
560 float: crate::Scalar,
561 int: crate::Scalar,
562) -> (crate::Literal, crate::Literal) {
563 match (float, int) {
564 (crate::Scalar::F16, crate::Scalar::I32) => (
565 crate::Literal::F16(i32::min_float()),
566 crate::Literal::F16(i32::max_float()),
567 ),
568 (crate::Scalar::F16, crate::Scalar::U32) => (
569 crate::Literal::F16(u32::min_float()),
570 crate::Literal::F16(u32::max_float()),
571 ),
572 (crate::Scalar::F16, crate::Scalar::I64) => (
573 crate::Literal::F16(i64::min_float()),
574 crate::Literal::F16(i64::max_float()),
575 ),
576 (crate::Scalar::F16, crate::Scalar::U64) => (
577 crate::Literal::F16(u64::min_float()),
578 crate::Literal::F16(u64::max_float()),
579 ),
580 (crate::Scalar::F32, crate::Scalar::I32) => (
581 crate::Literal::F32(i32::min_float()),
582 crate::Literal::F32(i32::max_float()),
583 ),
584 (crate::Scalar::F32, crate::Scalar::U32) => (
585 crate::Literal::F32(u32::min_float()),
586 crate::Literal::F32(u32::max_float()),
587 ),
588 (crate::Scalar::F32, crate::Scalar::I64) => (
589 crate::Literal::F32(i64::min_float()),
590 crate::Literal::F32(i64::max_float()),
591 ),
592 (crate::Scalar::F32, crate::Scalar::U64) => (
593 crate::Literal::F32(u64::min_float()),
594 crate::Literal::F32(u64::max_float()),
595 ),
596 (crate::Scalar::F64, crate::Scalar::I32) => (
597 crate::Literal::F64(i32::min_float()),
598 crate::Literal::F64(i32::max_float()),
599 ),
600 (crate::Scalar::F64, crate::Scalar::U32) => (
601 crate::Literal::F64(u32::min_float()),
602 crate::Literal::F64(u32::max_float()),
603 ),
604 (crate::Scalar::F64, crate::Scalar::I64) => (
605 crate::Literal::F64(i64::min_float()),
606 crate::Literal::F64(i64::max_float()),
607 ),
608 (crate::Scalar::F64, crate::Scalar::U64) => (
609 crate::Literal::F64(u64::min_float()),
610 crate::Literal::F64(u64::max_float()),
611 ),
612 _ => unreachable!(),
613 }
614}