1pub mod index;
6mod layouter;
7mod namer;
8mod terminator;
9mod typifier;
10
11pub use index::{BoundsCheckPolicies, BoundsCheckPolicy, IndexableLength, IndexableLengthError};
12pub use layouter::{Alignment, LayoutError, LayoutErrorInner, Layouter, TypeLayout};
13pub use namer::{EntryPointIndex, NameKey, Namer};
14pub use terminator::ensure_block_returns;
15pub use typifier::{ResolveContext, ResolveError, TypeResolution};
16
17impl From<super::StorageFormat> for super::ScalarKind {
18 fn from(format: super::StorageFormat) -> Self {
19 use super::{ScalarKind as Sk, StorageFormat as Sf};
20 match format {
21 Sf::R8Unorm => Sk::Float,
22 Sf::R8Snorm => Sk::Float,
23 Sf::R8Uint => Sk::Uint,
24 Sf::R8Sint => Sk::Sint,
25 Sf::R16Uint => Sk::Uint,
26 Sf::R16Sint => Sk::Sint,
27 Sf::R16Float => Sk::Float,
28 Sf::Rg8Unorm => Sk::Float,
29 Sf::Rg8Snorm => Sk::Float,
30 Sf::Rg8Uint => Sk::Uint,
31 Sf::Rg8Sint => Sk::Sint,
32 Sf::R32Uint => Sk::Uint,
33 Sf::R32Sint => Sk::Sint,
34 Sf::R32Float => Sk::Float,
35 Sf::Rg16Uint => Sk::Uint,
36 Sf::Rg16Sint => Sk::Sint,
37 Sf::Rg16Float => Sk::Float,
38 Sf::Rgba8Unorm => Sk::Float,
39 Sf::Rgba8Snorm => Sk::Float,
40 Sf::Rgba8Uint => Sk::Uint,
41 Sf::Rgba8Sint => Sk::Sint,
42 Sf::Rgb10a2Unorm => Sk::Float,
43 Sf::Rg11b10Float => Sk::Float,
44 Sf::Rg32Uint => Sk::Uint,
45 Sf::Rg32Sint => Sk::Sint,
46 Sf::Rg32Float => Sk::Float,
47 Sf::Rgba16Uint => Sk::Uint,
48 Sf::Rgba16Sint => Sk::Sint,
49 Sf::Rgba16Float => Sk::Float,
50 Sf::Rgba32Uint => Sk::Uint,
51 Sf::Rgba32Sint => Sk::Sint,
52 Sf::Rgba32Float => Sk::Float,
53 Sf::R16Unorm => Sk::Float,
54 Sf::R16Snorm => Sk::Float,
55 Sf::Rg16Unorm => Sk::Float,
56 Sf::Rg16Snorm => Sk::Float,
57 Sf::Rgba16Unorm => Sk::Float,
58 Sf::Rgba16Snorm => Sk::Float,
59 }
60 }
61}
62
63impl super::ScalarKind {
64 pub const fn is_numeric(self) -> bool {
65 match self {
66 crate::ScalarKind::Sint | crate::ScalarKind::Uint | crate::ScalarKind::Float => true,
67 crate::ScalarKind::Bool => false,
68 }
69 }
70}
71
72impl PartialEq for crate::Literal {
73 fn eq(&self, other: &Self) -> bool {
74 match (*self, *other) {
75 (Self::F64(a), Self::F64(b)) => a.to_bits() == b.to_bits(),
76 (Self::F32(a), Self::F32(b)) => a.to_bits() == b.to_bits(),
77 (Self::U32(a), Self::U32(b)) => a == b,
78 (Self::I32(a), Self::I32(b)) => a == b,
79 (Self::Bool(a), Self::Bool(b)) => a == b,
80 _ => false,
81 }
82 }
83}
84impl Eq for crate::Literal {}
85impl std::hash::Hash for crate::Literal {
86 fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
87 match *self {
88 Self::F64(v) => {
89 hasher.write_u8(0);
90 v.to_bits().hash(hasher);
91 }
92 Self::F32(v) => {
93 hasher.write_u8(1);
94 v.to_bits().hash(hasher);
95 }
96 Self::U32(v) => {
97 hasher.write_u8(2);
98 v.hash(hasher);
99 }
100 Self::I32(v) => {
101 hasher.write_u8(3);
102 v.hash(hasher);
103 }
104 Self::Bool(v) => {
105 hasher.write_u8(4);
106 v.hash(hasher);
107 }
108 }
109 }
110}
111
112impl crate::Literal {
113 pub const fn new(value: u8, kind: crate::ScalarKind, width: crate::Bytes) -> Option<Self> {
114 match (value, kind, width) {
115 (value, crate::ScalarKind::Float, 8) => Some(Self::F64(value as _)),
116 (value, crate::ScalarKind::Float, 4) => Some(Self::F32(value as _)),
117 (value, crate::ScalarKind::Uint, 4) => Some(Self::U32(value as _)),
118 (value, crate::ScalarKind::Sint, 4) => Some(Self::I32(value as _)),
119 (1, crate::ScalarKind::Bool, 4) => Some(Self::Bool(true)),
120 (0, crate::ScalarKind::Bool, 4) => Some(Self::Bool(false)),
121 _ => None,
122 }
123 }
124
125 pub const fn zero(kind: crate::ScalarKind, width: crate::Bytes) -> Option<Self> {
126 Self::new(0, kind, width)
127 }
128
129 pub const fn one(kind: crate::ScalarKind, width: crate::Bytes) -> Option<Self> {
130 Self::new(1, kind, width)
131 }
132
133 pub const fn width(&self) -> crate::Bytes {
134 match *self {
135 Self::F64(_) => 8,
136 Self::F32(_) | Self::U32(_) | Self::I32(_) => 4,
137 Self::Bool(_) => 1,
138 }
139 }
140 pub const fn scalar_kind(&self) -> crate::ScalarKind {
141 match *self {
142 Self::F64(_) | Self::F32(_) => crate::ScalarKind::Float,
143 Self::U32(_) => crate::ScalarKind::Uint,
144 Self::I32(_) => crate::ScalarKind::Sint,
145 Self::Bool(_) => crate::ScalarKind::Bool,
146 }
147 }
148 pub const fn ty_inner(&self) -> crate::TypeInner {
149 crate::TypeInner::Scalar {
150 kind: self.scalar_kind(),
151 width: self.width(),
152 }
153 }
154}
155
156pub const POINTER_SPAN: u32 = 4;
157
158impl super::TypeInner {
159 pub const fn scalar_kind(&self) -> Option<super::ScalarKind> {
160 match *self {
161 super::TypeInner::Scalar { kind, .. } | super::TypeInner::Vector { kind, .. } => {
162 Some(kind)
163 }
164 super::TypeInner::Matrix { .. } => Some(super::ScalarKind::Float),
165 _ => None,
166 }
167 }
168
169 pub const fn scalar_width(&self) -> Option<u8> {
170 match *self {
172 super::TypeInner::Scalar { width, .. } | super::TypeInner::Vector { width, .. } => {
173 Some(width * 8)
174 }
175 super::TypeInner::Matrix { width, .. } => Some(width * 8),
176 _ => None,
177 }
178 }
179
180 pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
181 match *self {
182 Self::Pointer { space, .. } => Some(space),
183 Self::ValuePointer { space, .. } => Some(space),
184 _ => None,
185 }
186 }
187
188 pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
189 match *self {
190 crate::TypeInner::Pointer { base, .. } => match types[base].inner {
191 crate::TypeInner::Atomic { .. } => true,
192 _ => false,
193 },
194 _ => false,
195 }
196 }
197
198 pub fn size(&self, _gctx: GlobalCtx) -> u32 {
200 match *self {
201 Self::Scalar { kind: _, width } | Self::Atomic { kind: _, width } => width as u32,
202 Self::Vector {
203 size,
204 kind: _,
205 width,
206 } => size as u32 * width as u32,
207 Self::Matrix {
209 columns,
210 rows,
211 width,
212 } => Alignment::from(rows) * width as u32 * columns as u32,
213 Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN,
214 Self::Array {
215 base: _,
216 size,
217 stride,
218 } => {
219 let count = match size {
220 super::ArraySize::Constant(count) => count.get(),
221 super::ArraySize::Dynamic => 1,
223 };
224 count * stride
225 }
226 Self::Struct { span, .. } => span,
227 Self::Image { .. }
228 | Self::Sampler { .. }
229 | Self::AccelerationStructure
230 | Self::RayQuery
231 | Self::BindingArray { .. } => 0,
232 }
233 }
234
235 pub fn canonical_form(
244 &self,
245 types: &crate::UniqueArena<crate::Type>,
246 ) -> Option<crate::TypeInner> {
247 use crate::TypeInner as Ti;
248 match *self {
249 Ti::Pointer { base, space } => match types[base].inner {
250 Ti::Scalar { kind, width } => Some(Ti::ValuePointer {
251 size: None,
252 kind,
253 width,
254 space,
255 }),
256 Ti::Vector { size, kind, width } => Some(Ti::ValuePointer {
257 size: Some(size),
258 kind,
259 width,
260 space,
261 }),
262 _ => None,
263 },
264 _ => None,
265 }
266 }
267
268 pub fn equivalent(
277 &self,
278 rhs: &crate::TypeInner,
279 types: &crate::UniqueArena<crate::Type>,
280 ) -> bool {
281 let left = self.canonical_form(types);
282 let right = rhs.canonical_form(types);
283 left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
284 }
285
286 pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
287 use crate::TypeInner as Ti;
288 match *self {
289 Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
290 Ti::Struct { ref members, .. } => members
291 .last()
292 .map(|last| types[last.ty].inner.is_dynamically_sized(types))
293 .unwrap_or(false),
294 _ => false,
295 }
296 }
297
298 pub fn components(&self) -> Option<u32> {
299 Some(match *self {
300 Self::Vector { size, .. } => size as u32,
301 Self::Matrix { columns, .. } => columns as u32,
302 Self::Array {
303 size: crate::ArraySize::Constant(len),
304 ..
305 } => len.get(),
306 Self::Struct { ref members, .. } => members.len() as u32,
307 _ => return None,
308 })
309 }
310
311 pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
312 Some(match *self {
313 Self::Vector { kind, width, .. } => {
314 TypeResolution::Value(crate::TypeInner::Scalar { kind, width })
315 }
316 Self::Matrix { rows, width, .. } => TypeResolution::Value(crate::TypeInner::Vector {
317 size: rows,
318 kind: crate::ScalarKind::Float,
319 width,
320 }),
321 Self::Array {
322 base,
323 size: crate::ArraySize::Constant(_),
324 ..
325 } => TypeResolution::Handle(base),
326 Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
327 _ => return None,
328 })
329 }
330}
331
332impl super::AddressSpace {
333 pub fn access(self) -> crate::StorageAccess {
334 use crate::StorageAccess as Sa;
335 match self {
336 crate::AddressSpace::Function
337 | crate::AddressSpace::Private
338 | crate::AddressSpace::WorkGroup => Sa::LOAD | Sa::STORE,
339 crate::AddressSpace::Uniform => Sa::LOAD,
340 crate::AddressSpace::Storage { access } => access,
341 crate::AddressSpace::Handle => Sa::LOAD,
342 crate::AddressSpace::PushConstant => Sa::LOAD,
343 }
344 }
345}
346
347impl super::MathFunction {
348 pub const fn argument_count(&self) -> usize {
349 match *self {
350 Self::Abs => 1,
352 Self::Min => 2,
353 Self::Max => 2,
354 Self::Clamp => 3,
355 Self::Saturate => 1,
356 Self::Cos => 1,
358 Self::Cosh => 1,
359 Self::Sin => 1,
360 Self::Sinh => 1,
361 Self::Tan => 1,
362 Self::Tanh => 1,
363 Self::Acos => 1,
364 Self::Asin => 1,
365 Self::Atan => 1,
366 Self::Atan2 => 2,
367 Self::Asinh => 1,
368 Self::Acosh => 1,
369 Self::Atanh => 1,
370 Self::Radians => 1,
371 Self::Degrees => 1,
372 Self::Ceil => 1,
374 Self::Floor => 1,
375 Self::Round => 1,
376 Self::Fract => 1,
377 Self::Trunc => 1,
378 Self::Modf => 2,
379 Self::Frexp => 2,
380 Self::Ldexp => 2,
381 Self::Exp => 1,
383 Self::Exp2 => 1,
384 Self::Log => 1,
385 Self::Log2 => 1,
386 Self::Pow => 2,
387 Self::Dot => 2,
389 Self::Outer => 2,
390 Self::Cross => 2,
391 Self::Distance => 2,
392 Self::Length => 1,
393 Self::Normalize => 1,
394 Self::FaceForward => 3,
395 Self::Reflect => 2,
396 Self::Refract => 3,
397 Self::Sign => 1,
399 Self::Fma => 3,
400 Self::Mix => 3,
401 Self::Step => 2,
402 Self::SmoothStep => 3,
403 Self::Sqrt => 1,
404 Self::InverseSqrt => 1,
405 Self::Inverse => 1,
406 Self::Transpose => 1,
407 Self::Determinant => 1,
408 Self::CountTrailingZeros => 1,
410 Self::CountLeadingZeros => 1,
411 Self::CountOneBits => 1,
412 Self::ReverseBits => 1,
413 Self::ExtractBits => 3,
414 Self::InsertBits => 4,
415 Self::FindLsb => 1,
416 Self::FindMsb => 1,
417 Self::Pack4x8snorm => 1,
419 Self::Pack4x8unorm => 1,
420 Self::Pack2x16snorm => 1,
421 Self::Pack2x16unorm => 1,
422 Self::Pack2x16float => 1,
423 Self::Unpack4x8snorm => 1,
425 Self::Unpack4x8unorm => 1,
426 Self::Unpack2x16snorm => 1,
427 Self::Unpack2x16unorm => 1,
428 Self::Unpack2x16float => 1,
429 }
430 }
431}
432
433impl crate::Expression {
434 pub const fn needs_pre_emit(&self) -> bool {
436 match *self {
437 Self::Literal(_)
438 | Self::Constant(_)
439 | Self::ZeroValue(_)
440 | Self::FunctionArgument(_)
441 | Self::GlobalVariable(_)
442 | Self::LocalVariable(_) => true,
443 _ => false,
444 }
445 }
446
447 pub fn is_dynamic_index(&self, module: &crate::Module) -> bool {
461 if let Self::Constant(handle) = *self {
462 let constant = &module.constants[handle];
463 !matches!(constant.r#override, crate::Override::None)
464 } else {
465 true
466 }
467 }
468}
469
470impl crate::Function {
471 pub fn originating_global(
480 &self,
481 mut pointer: crate::Handle<crate::Expression>,
482 ) -> Option<crate::Handle<crate::GlobalVariable>> {
483 loop {
484 pointer = match self.expressions[pointer] {
485 crate::Expression::Access { base, .. } => base,
486 crate::Expression::AccessIndex { base, .. } => base,
487 crate::Expression::GlobalVariable(handle) => return Some(handle),
488 crate::Expression::LocalVariable(_) => return None,
489 crate::Expression::FunctionArgument(_) => return None,
490 _ => unreachable!(),
492 }
493 }
494 }
495}
496
497impl crate::SampleLevel {
498 pub const fn implicit_derivatives(&self) -> bool {
499 match *self {
500 Self::Auto | Self::Bias(_) => true,
501 Self::Zero | Self::Exact(_) | Self::Gradient { .. } => false,
502 }
503 }
504}
505
506impl crate::Binding {
507 pub const fn to_built_in(&self) -> Option<crate::BuiltIn> {
508 match *self {
509 crate::Binding::BuiltIn(built_in) => Some(built_in),
510 Self::Location { .. } => None,
511 }
512 }
513}
514
515impl super::SwizzleComponent {
516 pub const XYZW: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];
517
518 pub const fn index(&self) -> u32 {
519 match *self {
520 Self::X => 0,
521 Self::Y => 1,
522 Self::Z => 2,
523 Self::W => 3,
524 }
525 }
526 pub const fn from_index(idx: u32) -> Self {
527 match idx {
528 0 => Self::X,
529 1 => Self::Y,
530 2 => Self::Z,
531 _ => Self::W,
532 }
533 }
534}
535
536impl super::ImageClass {
537 pub const fn is_multisampled(self) -> bool {
538 match self {
539 crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => multi,
540 crate::ImageClass::Storage { .. } => false,
541 }
542 }
543
544 pub const fn is_mipmapped(self) -> bool {
545 match self {
546 crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => !multi,
547 crate::ImageClass::Storage { .. } => false,
548 }
549 }
550}
551
552impl crate::Module {
553 pub const fn to_ctx(&self) -> GlobalCtx<'_> {
554 GlobalCtx {
555 types: &self.types,
556 constants: &self.constants,
557 const_expressions: &self.const_expressions,
558 }
559 }
560}
561
562#[derive(Debug)]
563pub(super) enum U32EvalError {
564 NonConst,
565 Negative,
566}
567
568#[derive(Clone, Copy)]
569pub struct GlobalCtx<'a> {
570 pub types: &'a crate::UniqueArena<crate::Type>,
571 pub constants: &'a crate::Arena<crate::Constant>,
572 pub const_expressions: &'a crate::Arena<crate::Expression>,
573}
574
575impl GlobalCtx<'_> {
576 #[allow(dead_code)]
578 pub(super) fn eval_expr_to_u32(
579 &self,
580 handle: crate::Handle<crate::Expression>,
581 ) -> Result<u32, U32EvalError> {
582 self.eval_expr_to_u32_from(handle, self.const_expressions)
583 }
584
585 pub(super) fn eval_expr_to_u32_from(
587 &self,
588 handle: crate::Handle<crate::Expression>,
589 arena: &crate::Arena<crate::Expression>,
590 ) -> Result<u32, U32EvalError> {
591 fn get(
592 gctx: GlobalCtx,
593 handle: crate::Handle<crate::Expression>,
594 arena: &crate::Arena<crate::Expression>,
595 ) -> Result<u32, U32EvalError> {
596 match arena[handle] {
597 crate::Expression::Literal(crate::Literal::U32(value)) => Ok(value),
598 crate::Expression::Literal(crate::Literal::I32(value)) => {
599 value.try_into().map_err(|_| U32EvalError::Negative)
600 }
601 crate::Expression::ZeroValue(ty)
602 if matches!(
603 gctx.types[ty].inner,
604 crate::TypeInner::Scalar {
605 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
606 width: _
607 }
608 ) =>
609 {
610 Ok(0)
611 }
612 _ => Err(U32EvalError::NonConst),
613 }
614 }
615 match arena[handle] {
616 crate::Expression::Constant(c) => {
617 get(*self, self.constants[c].init, self.const_expressions)
618 }
619 _ => get(*self, handle, arena),
620 }
621 }
622}
623
624#[test]
625fn test_matrix_size() {
626 let module = crate::Module::default();
627 assert_eq!(
628 crate::TypeInner::Matrix {
629 columns: crate::VectorSize::Tri,
630 rows: crate::VectorSize::Tri,
631 width: 4
632 }
633 .size(module.to_ctx()),
634 48,
635 );
636}