wgsl_types/builtin/
call.rs

1//! Built-in functions call implementations.
2//!
3//! Functions bear the same name as the WGSL counterpart.
4//! Functions that take template parameters are suffixed with `_t` and take `tplt_*` arguments.
5//!
6//! ### Warning
7//!
8//! Some functions are still TODO and are documented as such.
9//! Derivatives and texture functions are also missing.
10//!
11//! ### Usage quirks
12//!
13//! * The arguments must be [loaded][Type::loaded].
14//! * User-defined functions can shadow WGSL built-in functions.
15//! * Type aliases must be resolved: WGSL allows calling functions with the name of the alias.
16
17#![allow(non_snake_case)]
18
19use half::prelude::*;
20use itertools::{Itertools, chain, izip};
21use num_traits::{One, ToBytes, ToPrimitive, Zero, real::Real};
22
23use crate::{
24    Error, Instance, ShaderStage,
25    conv::{Convert, convert, convert_all_ty},
26    inst::{
27        AtomicInstance, LiteralInstance, MatInstance, RefInstance, StructInstance, VecInstance,
28    },
29    ty::{Ty, Type},
30};
31
32use super::{Compwise, atomic_compare_exchange_struct_type, frexp_struct_type};
33
34type E = Error;
35
36// -------
37// BITCAST
38// -------
39// reference: <https://www.w3.org/TR/WGSL/#bit-reinterp-builtin-functions>
40
41/// `bitcast<T>()` builtin function.
42///
43/// Reference: <https://www.w3.org/TR/WGSL/#bitcast-builtin>
44pub fn bitcast_t(tplt_ty: &Type, e: &Instance) -> Result<Instance, E> {
45    fn lit_bytes(l: &LiteralInstance, ty: &Type) -> Result<Vec<u8>, E> {
46        match l {
47            LiteralInstance::Bool(_) => Err(E::Builtin("bitcast argument cannot be bool")),
48            LiteralInstance::AbstractInt(n) => {
49                if ty == &Type::U32 {
50                    n.to_u32()
51                        .map(|n| n.to_le_bytes().to_vec())
52                        .ok_or(E::ConvOverflow(*l, Type::U32))
53                } else {
54                    n.to_i32()
55                        .map(|n| n.to_le_bytes().to_vec())
56                        .ok_or(E::ConvOverflow(*l, Type::I32))
57                }
58            }
59            LiteralInstance::AbstractFloat(n) => n
60                .to_f32()
61                .map(|n| n.to_le_bytes().to_vec())
62                .ok_or(E::ConvOverflow(*l, Type::F32)),
63            LiteralInstance::I32(n) => Ok(n.to_le_bytes().to_vec()),
64            LiteralInstance::U32(n) => Ok(n.to_le_bytes().to_vec()),
65            LiteralInstance::F32(n) => Ok(n.to_le_bytes().to_vec()),
66            LiteralInstance::F16(n) => Ok(n.to_le_bytes().to_vec()),
67            #[cfg(feature = "naga-ext")]
68            LiteralInstance::I64(n) => Ok(n.to_le_bytes().to_vec()),
69            #[cfg(feature = "naga-ext")]
70            LiteralInstance::U64(n) => Ok(n.to_le_bytes().to_vec()),
71            #[cfg(feature = "naga-ext")]
72            LiteralInstance::F64(n) => Ok(n.to_le_bytes().to_vec()),
73        }
74    }
75
76    fn vec_bytes(v: &VecInstance, ty: &Type) -> Result<Vec<u8>, E> {
77        v.iter()
78            .map(|n| lit_bytes(n.unwrap_literal_ref(), ty))
79            .reduce(|n1, n2| Ok(chain(n1?, n2?).collect_vec()))
80            .unwrap()
81    }
82
83    let inner_ty = tplt_ty.inner_ty();
84
85    let bytes = match e {
86        Instance::Literal(l) => lit_bytes(l, &inner_ty),
87        Instance::Vec(v) => vec_bytes(v, &inner_ty),
88        _ => Err(E::Builtin(
89            "`bitcast` expects a numeric scalar or vector argument",
90        )),
91    }?;
92
93    let size_err = E::Builtin("`bitcast` input and output types must have the same size");
94
95    match tplt_ty {
96        Type::I32 => {
97            let n = i32::from_le_bytes(bytes.try_into().map_err(|_| size_err)?);
98            Ok(LiteralInstance::I32(n).into())
99        }
100        Type::U32 => {
101            let n = u32::from_le_bytes(bytes.try_into().map_err(|_| size_err)?);
102            Ok(LiteralInstance::U32(n).into())
103        }
104        Type::F32 => {
105            let n = f32::from_le_bytes(bytes.try_into().map_err(|_| size_err)?);
106            Ok(LiteralInstance::F32(n).into())
107        }
108        Type::F16 => {
109            let n = f16::from_le_bytes(bytes.try_into().map_err(|_| size_err)?);
110            Ok(LiteralInstance::F16(n).into())
111        }
112        Type::Vec(n, ty) => {
113            if **ty == Type::I32 && bytes.len() == 4 * (*n as usize) {
114                let v = bytes
115                    .chunks(4)
116                    .map(|b| i32::from_le_bytes(b.try_into().unwrap()))
117                    .map(|n| LiteralInstance::from(n).into())
118                    .collect_vec();
119                Ok(VecInstance::new(v).into())
120            } else if **ty == Type::U32 && bytes.len() == 4 * (*n as usize) {
121                let v = bytes
122                    .chunks(4)
123                    .map(|b| u32::from_le_bytes(b.try_into().unwrap()))
124                    .map(|n| LiteralInstance::from(n).into())
125                    .collect_vec();
126                Ok(VecInstance::new(v).into())
127            } else if **ty == Type::F32 && bytes.len() == 4 * (*n as usize) {
128                let v = bytes
129                    .chunks(4)
130                    .map(|b| f32::from_le_bytes(b.try_into().unwrap()))
131                    .map(|n| LiteralInstance::from(n).into())
132                    .collect_vec();
133                Ok(VecInstance::new(v).into())
134            } else if **ty == Type::F16 && bytes.len() == 2 * (*n as usize) {
135                let v = bytes
136                    .chunks(2)
137                    .map(|b| f16::from_le_bytes(b.try_into().unwrap()))
138                    .map(|n| LiteralInstance::from(n).into())
139                    .collect_vec();
140                Ok(VecInstance::new(v).into())
141            } else {
142                Err(size_err)
143            }
144        }
145        _ => unreachable!("invalid `bitcast` template"),
146    }
147}
148
149// -------
150// LOGICAL
151// -------
152// reference: <https://www.w3.org/TR/WGSL/#logical-builtin-functions>
153
154/// `all()` builtin function.
155///
156/// Reference: <https://www.w3.org/TR/WGSL/#all-builtin>
157pub fn all(e: &Instance) -> Result<Instance, E> {
158    match e {
159        Instance::Literal(LiteralInstance::Bool(_)) => Ok(e.clone()),
160        Instance::Vec(v) if v.inner_ty() == Type::Bool => {
161            let b = v.iter().all(|b| b.unwrap_literal_ref().unwrap_bool());
162            Ok(LiteralInstance::Bool(b).into())
163        }
164        _ => Err(E::Builtin(
165            "`all` expects a boolean or vector of boolean argument",
166        )),
167    }
168}
169
170/// `any()` builtin function.
171///
172/// Reference: <https://www.w3.org/TR/WGSL/#any-builtin>
173pub fn any(e: &Instance) -> Result<Instance, E> {
174    match e {
175        Instance::Literal(LiteralInstance::Bool(_)) => Ok(e.clone()),
176        Instance::Vec(v) if v.inner_ty() == Type::Bool => {
177            let b = v.iter().any(|b| b.unwrap_literal_ref().unwrap_bool());
178            Ok(LiteralInstance::Bool(b).into())
179        }
180        _ => Err(E::Builtin(
181            "`any` expects a boolean or vector of boolean argument",
182        )),
183    }
184}
185
186/// `select()` builtin function.
187///
188/// Reference: <https://www.w3.org/TR/WGSL/#select-builtin>
189pub fn select(f: &Instance, t: &Instance, cond: &Instance) -> Result<Instance, E> {
190    let (f, t) = convert(f, t).ok_or(E::Builtin(
191        "`select` 1st and 2nd arguments are incompatible",
192    ))?;
193
194    match cond {
195        Instance::Literal(LiteralInstance::Bool(b)) => Ok(b.then_some(t).unwrap_or(f)),
196        Instance::Vec(v) if v.inner_ty() == Type::Bool => match (f, t) {
197            (Instance::Vec(v1), Instance::Vec(v2)) => {
198                if v1.n() != v.n() {
199                    Err(E::Builtin(
200                        "`select` vector arguments must have the same number of components",
201                    ))
202                } else {
203                    let v = izip!(v1, v2, v.iter())
204                        .map(|(f, t, b)| {
205                            if b.unwrap_literal_ref().unwrap_bool() {
206                                t.to_owned() // BUG: is it a bug in rust_analyzer? it displays f as Instance and t as &Instance
207                            } else {
208                                f.to_owned()
209                            }
210                        })
211                        .collect_vec();
212                    Ok(VecInstance::new(v).into())
213                }
214            }
215            _ => Err(E::Builtin(
216                "`select` arguments must be vectors when the condition is a vector",
217            )),
218        },
219        _ => Err(E::Builtin(
220            "`select` 3rd argument must be a boolean or vector of boolean",
221        )),
222    }
223}
224
225// -----
226// ARRAY
227// -----
228// reference: <https://www.w3.org/TR/WGSL/#array-builtin-functions>
229
230/// `arrayLength()` builtin function.
231///
232/// Reference: <https://www.w3.org/TR/WGSL/#arrayLength-builtin>
233pub fn arrayLength(p: &Instance) -> Result<Instance, E> {
234    let err = E::Builtin("`arrayLength` expects a pointer to array argument");
235    let r = match p {
236        Instance::Ptr(p) => RefInstance::from(p.clone()),
237        _ => return Err(err),
238    };
239    let r = r.read()?;
240    match &*r {
241        Instance::Array(a) => Ok(LiteralInstance::U32(a.n() as u32).into()),
242        _ => Err(err),
243    }
244}
245
246// -------
247// NUMERIC
248// -------
249// reference: <https://www.w3.org/TR/WGSL/#numeric-builtin-function>
250
251macro_rules! impl_call_float_unary {
252    ($name:literal, $e:ident, $n:ident => $expr:expr) => {{
253        const ERR: E = E::Builtin(concat!(
254            "`",
255            $name,
256            "` expects a float or vector of float argument"
257        ));
258        fn lit_fn(l: &LiteralInstance) -> Result<LiteralInstance, E> {
259            match l {
260                LiteralInstance::Bool(_) => Err(ERR),
261                LiteralInstance::AbstractInt(_) => {
262                    let $n = l
263                        .convert_to(&Type::AbstractFloat)
264                        .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?
265                        .unwrap_abstract_float();
266                    Ok(LiteralInstance::from($expr))
267                }
268                LiteralInstance::AbstractFloat($n) => Ok(LiteralInstance::from($expr)),
269                LiteralInstance::I32(_) => Err(ERR),
270                LiteralInstance::U32(_) => Err(ERR),
271                LiteralInstance::F32($n) => Ok(LiteralInstance::from($expr)),
272                LiteralInstance::F16($n) => Ok(LiteralInstance::from($expr)),
273                #[cfg(feature = "naga-ext")]
274                LiteralInstance::I64(_) => Err(ERR),
275                #[cfg(feature = "naga-ext")]
276                LiteralInstance::U64(_) => Err(ERR),
277                #[cfg(feature = "naga-ext")]
278                LiteralInstance::F64($n) => Ok(LiteralInstance::F64($expr)),
279            }
280        }
281        match $e {
282            Instance::Literal(l) => lit_fn(l).map(Into::into),
283            Instance::Vec(v) => v.compwise_unary(lit_fn).map(Into::into),
284            _ => Err(ERR),
285        }
286    }};
287}
288
289/// `abs()` builtin function.
290///
291/// Reference: <https://www.w3.org/TR/WGSL/#abs-builtin>
292// TODO: use checked_abs
293pub fn abs(e: &Instance) -> Result<Instance, E> {
294    const ERR: E = E::Builtin("`abs` expects a scalar or vector of scalar argument");
295    fn lit_abs(l: &LiteralInstance) -> Result<LiteralInstance, E> {
296        match l {
297            LiteralInstance::Bool(_) => Err(ERR),
298            LiteralInstance::AbstractInt(n) => Ok(LiteralInstance::from(n.wrapping_abs())),
299            LiteralInstance::AbstractFloat(n) => Ok(LiteralInstance::from(n.abs())),
300            LiteralInstance::I32(n) => Ok(LiteralInstance::from(n.wrapping_abs())),
301            LiteralInstance::U32(_) => Ok(*l),
302            LiteralInstance::F32(n) => Ok(LiteralInstance::from(n.abs())),
303            LiteralInstance::F16(n) => Ok(LiteralInstance::from(n.abs())),
304            #[cfg(feature = "naga-ext")]
305            LiteralInstance::I64(n) => Ok(LiteralInstance::I64(n.wrapping_abs())),
306            #[cfg(feature = "naga-ext")]
307            LiteralInstance::U64(_) => Ok(*l),
308            #[cfg(feature = "naga-ext")]
309            LiteralInstance::F64(n) => Ok(LiteralInstance::F64(n.abs())),
310        }
311    }
312    match e {
313        Instance::Literal(l) => lit_abs(l).map(Into::into),
314        Instance::Vec(v) => v.compwise_unary(lit_abs).map(Into::into),
315        _ => Err(ERR),
316    }
317}
318
319/// `acos()` builtin function.
320///
321/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
322///
323/// Reference: <https://www.w3.org/TR/WGSL/#acos-builtin>
324pub fn acos(e: &Instance) -> Result<Instance, E> {
325    impl_call_float_unary!("acos", e, n => n.acos())
326}
327
328/// `acosh()` builtin function.
329///
330/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
331///
332/// Reference: <https://www.w3.org/TR/WGSL/#acosh-builtin>
333pub fn acosh(e: &Instance) -> Result<Instance, E> {
334    // TODO: Rust's acosh implementation overflows for inputs close to max_float.
335    // it's no big deal, but some cts tests fail because of that.
336    impl_call_float_unary!("acosh", e, n => n.acosh())
337}
338
339/// `asin()` builtin function.
340///
341/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
342///
343/// Reference: <https://www.w3.org/TR/WGSL/#asin-builtin>
344pub fn asin(e: &Instance) -> Result<Instance, E> {
345    impl_call_float_unary!("asin", e, n => n.asin())
346}
347
348/// `asinh()` builtin function.
349///
350/// Reference: <https://www.w3.org/TR/WGSL/#asinh-builtin>
351pub fn asinh(e: &Instance) -> Result<Instance, E> {
352    // TODO: Rust's asinh implementation overflows for inputs close to max_float.
353    // it's no big deal, but some cts tests fail because of that.
354    impl_call_float_unary!("asinh", e, n => n.asinh())
355}
356
357/// `atan()` builtin function.
358///
359/// Reference: <https://www.w3.org/TR/WGSL/#atan-builtin>
360pub fn atan(e: &Instance) -> Result<Instance, E> {
361    impl_call_float_unary!("atan", e, n => n.atan())
362}
363
364/// `atanh()` builtin function.
365///
366/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
367///
368/// Reference: <https://www.w3.org/TR/WGSL/#atanh-builtin>
369pub fn atanh(e: &Instance) -> Result<Instance, E> {
370    impl_call_float_unary!("atanh", e, n => n.atanh())
371}
372
373/// `atan2()` builtin function.
374///
375/// Reference: <https://www.w3.org/TR/WGSL/#atan2-builtin>
376pub fn atan2(y: &Instance, x: &Instance) -> Result<Instance, E> {
377    const ERR: E = E::Builtin("`atan2` expects a float or vector of float argument");
378    fn lit_atan2(y: &LiteralInstance, x: &LiteralInstance) -> Result<LiteralInstance, E> {
379        match y {
380            LiteralInstance::Bool(_) => Err(ERR),
381            LiteralInstance::AbstractInt(_) => {
382                let y = y
383                    .convert_to(&Type::AbstractFloat)
384                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?;
385                let x = x
386                    .convert_to(&Type::AbstractFloat)
387                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?;
388                Ok(LiteralInstance::from(
389                    y.unwrap_abstract_float().atan2(x.unwrap_abstract_float()),
390                ))
391            }
392            LiteralInstance::AbstractFloat(y) => {
393                Ok(LiteralInstance::from(y.atan2(x.unwrap_abstract_float())))
394            }
395            LiteralInstance::I32(_) => Err(ERR),
396            LiteralInstance::U32(_) => Err(ERR),
397            LiteralInstance::F32(y) => Ok(LiteralInstance::from(y.atan2(x.unwrap_f32()))),
398            LiteralInstance::F16(y) => Ok(LiteralInstance::from(y.atan2(x.unwrap_f16()))),
399            #[cfg(feature = "naga-ext")]
400            LiteralInstance::I64(_) => Err(ERR),
401            #[cfg(feature = "naga-ext")]
402            LiteralInstance::U64(_) => Err(ERR),
403            #[cfg(feature = "naga-ext")]
404            LiteralInstance::F64(y) => Ok(LiteralInstance::F64(y.atan2(x.unwrap_f64()))),
405        }
406    }
407    let (y, x) = convert(y, x).ok_or(E::Builtin("`atan2` arguments are incompatible"))?;
408    match (y, x) {
409        (Instance::Literal(y), Instance::Literal(x)) => lit_atan2(&y, &x).map(Into::into),
410        (Instance::Vec(y), Instance::Vec(x)) => y.compwise_binary(&x, lit_atan2).map(Into::into),
411        _ => Err(ERR),
412    }
413}
414
415/// `ceil()` builtin function.
416///
417/// Reference: <https://www.w3.org/TR/WGSL/#ceil-builtin>
418pub fn ceil(e: &Instance) -> Result<Instance, E> {
419    impl_call_float_unary!("ceil", e, n => n.ceil())
420}
421
422/// `clamp()` builtin function.
423///
424/// Reference: <https://www.w3.org/TR/WGSL/#clamp-builtin>
425pub fn clamp(e: &Instance, low: &Instance, high: &Instance) -> Result<Instance, E> {
426    const ERR: E = E::Builtin("`clamp` arguments are incompatible");
427    let tys = [e.ty(), low.ty(), high.ty()];
428    let ty = convert_all_ty(&tys).ok_or(ERR)?;
429    let e = e.convert_to(ty).ok_or(ERR)?;
430    let low = low.convert_to(ty).ok_or(ERR)?;
431    let high = high.convert_to(ty).ok_or(ERR)?;
432    min(&max(&e, &low)?, &high)
433}
434
435/// `cos()` builtin function.
436///
437/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
438///
439/// Reference: <https://www.w3.org/TR/WGSL/#cos-builtin>
440pub fn cos(e: &Instance) -> Result<Instance, E> {
441    impl_call_float_unary!("cos", e, n => n.cos())
442}
443
444/// `cosh()` builtin function.
445///
446/// Reference: <https://www.w3.org/TR/WGSL/#cosh-builtin>
447pub fn cosh(e: &Instance) -> Result<Instance, E> {
448    impl_call_float_unary!("cosh", e, n => n.cosh())
449}
450
451/// `countLeadingZeros()` builtin function.
452///
453/// Reference: <https://www.w3.org/TR/WGSL/#countLeadingZeros-builtin>
454pub fn countLeadingZeros(e: &Instance) -> Result<Instance, E> {
455    const ERR: E = E::Builtin("`countLeadingZeros` expects a float or vector of float argument");
456    fn lit_leading_zeros(l: &LiteralInstance) -> Result<LiteralInstance, E> {
457        match l {
458            LiteralInstance::Bool(_) => Err(ERR),
459            LiteralInstance::AbstractInt(n) => {
460                Ok(LiteralInstance::AbstractInt(n.leading_zeros() as i64))
461            }
462            LiteralInstance::AbstractFloat(_) => Err(ERR),
463            LiteralInstance::I32(n) => Ok(LiteralInstance::I32(n.leading_zeros() as i32)),
464            LiteralInstance::U32(n) => Ok(LiteralInstance::U32(n.leading_zeros())),
465            LiteralInstance::F32(_) => Err(ERR),
466            LiteralInstance::F16(_) => Err(ERR),
467            #[cfg(feature = "naga-ext")]
468            LiteralInstance::I64(n) => Ok(LiteralInstance::I64(n.leading_zeros() as i64)),
469            #[cfg(feature = "naga-ext")]
470            LiteralInstance::U64(n) => Ok(LiteralInstance::U64(n.leading_zeros() as u64)),
471            #[cfg(feature = "naga-ext")]
472            LiteralInstance::F64(_) => Err(ERR),
473        }
474    }
475    match e {
476        Instance::Literal(l) => lit_leading_zeros(l).map(Into::into),
477        Instance::Vec(v) => v.compwise_unary(lit_leading_zeros).map(Into::into),
478        _ => Err(ERR),
479    }
480}
481
482/// `countOneBits()` builtin function.
483///
484/// Reference: <https://www.w3.org/TR/WGSL/#countOneBits-builtin>
485pub fn countOneBits(e: &Instance) -> Result<Instance, E> {
486    const ERR: E = E::Builtin("`countOneBits` expects a float or vector of float argument");
487    fn lit_count_ones(l: &LiteralInstance) -> Result<LiteralInstance, E> {
488        match l {
489            LiteralInstance::Bool(_) => Err(ERR),
490            LiteralInstance::AbstractInt(n) => {
491                Ok(LiteralInstance::AbstractInt(n.count_ones() as i64))
492            }
493            LiteralInstance::AbstractFloat(_) => Err(ERR),
494            LiteralInstance::I32(n) => Ok(LiteralInstance::I32(n.count_ones() as i32)),
495            LiteralInstance::U32(n) => Ok(LiteralInstance::U32(n.count_ones())),
496            LiteralInstance::F32(_) => Err(ERR),
497            LiteralInstance::F16(_) => Err(ERR),
498            #[cfg(feature = "naga-ext")]
499            LiteralInstance::I64(n) => Ok(LiteralInstance::I64(n.count_ones() as i64)),
500            #[cfg(feature = "naga-ext")]
501            LiteralInstance::U64(n) => Ok(LiteralInstance::U64(n.count_ones() as u64)),
502            #[cfg(feature = "naga-ext")]
503            LiteralInstance::F64(_) => Err(ERR),
504        }
505    }
506    match e {
507        Instance::Literal(l) => lit_count_ones(l).map(Into::into),
508        Instance::Vec(v) => v.compwise_unary(lit_count_ones).map(Into::into),
509        _ => Err(ERR),
510    }
511}
512
513/// `countTrailingZeros()` builtin function.
514///
515/// Reference: <https://www.w3.org/TR/WGSL/#countTrailingZeros-builtin>
516pub fn countTrailingZeros(e: &Instance) -> Result<Instance, E> {
517    const ERR: E = E::Builtin("`countTrailingZeros` expects a float or vector of float argument");
518    fn lit_trailing_zeros(l: &LiteralInstance) -> Result<LiteralInstance, E> {
519        match l {
520            LiteralInstance::Bool(_) => Err(ERR),
521            LiteralInstance::AbstractInt(n) => {
522                Ok(LiteralInstance::AbstractInt(n.trailing_zeros() as i64))
523            }
524            LiteralInstance::AbstractFloat(_) => Err(ERR),
525            LiteralInstance::I32(n) => Ok(LiteralInstance::I32(n.trailing_zeros() as i32)),
526            LiteralInstance::U32(n) => Ok(LiteralInstance::U32(n.trailing_zeros())),
527            LiteralInstance::F32(_) => Err(ERR),
528            LiteralInstance::F16(_) => Err(ERR),
529            #[cfg(feature = "naga-ext")]
530            LiteralInstance::I64(n) => Ok(LiteralInstance::I64(n.trailing_zeros() as i64)),
531            #[cfg(feature = "naga-ext")]
532            LiteralInstance::U64(n) => Ok(LiteralInstance::U64(n.trailing_zeros() as u64)),
533            #[cfg(feature = "naga-ext")]
534            LiteralInstance::F64(_) => Err(ERR),
535        }
536    }
537    match e {
538        Instance::Literal(l) => lit_trailing_zeros(l).map(Into::into),
539        Instance::Vec(v) => v.compwise_unary(lit_trailing_zeros).map(Into::into),
540        _ => Err(ERR),
541    }
542}
543
544/// `cross()` builtin function.
545///
546/// Reference: <https://www.w3.org/TR/WGSL/#cross-builtin>
547pub fn cross(a: &Instance, b: &Instance, stage: ShaderStage) -> Result<Instance, E> {
548    let (a, b) = convert(a, b).ok_or(E::Builtin("`cross` arguments are incompatible"))?;
549    match (a, b) {
550        (Instance::Vec(a), Instance::Vec(b)) if a.n() == 3 => {
551            let s1 = a[1]
552                .op_mul(&b[2], stage)?
553                .op_sub(&a[2].op_mul(&b[1], stage)?, stage)?;
554            let s2 = a[2]
555                .op_mul(&b[0], stage)?
556                .op_sub(&a[0].op_mul(&b[2], stage)?, stage)?;
557            let s3 = a[0]
558                .op_mul(&b[1], stage)?
559                .op_sub(&a[1].op_mul(&b[0], stage)?, stage)?;
560            Ok(VecInstance::new(vec![s1, s2, s3]).into())
561        }
562        _ => Err(E::Builtin(
563            "`cross` expects a 3-component vector of float arguments",
564        )),
565    }
566}
567
568/// `degrees()` builtin function.
569///
570/// Reference: <https://www.w3.org/TR/WGSL/#degrees-builtin>
571pub fn degrees(e: &Instance) -> Result<Instance, E> {
572    impl_call_float_unary!("degrees", e, n => n.to_degrees())
573}
574
575/// TODO: This built-in is not implemented!
576pub fn determinant(_a1: &Instance) -> Result<Instance, E> {
577    Err(E::Todo("determinant".to_string()))
578}
579
580/// `distance()` builtin function.
581///
582/// NOTE: the function returns an error if computed out of domain
583///
584/// Reference: <https://www.w3.org/TR/WGSL/#distance-builtin>
585pub fn distance(e1: &Instance, e2: &Instance, stage: ShaderStage) -> Result<Instance, E> {
586    length(&e1.op_sub(e2, stage)?)
587}
588
589/// `dot()` builtin function.
590///
591/// Reference: <https://www.w3.org/TR/WGSL/#dot-builtin>
592pub fn dot(e1: &Instance, e2: &Instance, stage: ShaderStage) -> Result<Instance, E> {
593    let (e1, e2) = convert(e1, e2).ok_or(E::Builtin("`dot` arguments are incompatible"))?;
594    match (e1, e2) {
595        (Instance::Vec(e1), Instance::Vec(e2)) => e1.dot(&e2, stage).map(Into::into),
596        _ => Err(E::Builtin("`dot` expects vector arguments")),
597    }
598}
599
600/// TODO: This built-in is not implemented!
601pub fn dot4U8Packed(_a1: &Instance, _a2: &Instance) -> Result<Instance, E> {
602    Err(E::Todo("dot4U8Packed".to_string()))
603}
604
605/// TODO: This built-in is not implemented!
606pub fn dot4I8Packed(_a1: &Instance, _a2: &Instance) -> Result<Instance, E> {
607    Err(E::Todo("dot4I8Packed".to_string()))
608}
609
610/// `exp()` builtin function.
611///
612/// Reference: <https://www.w3.org/TR/WGSL/#exp-builtin>
613pub fn exp(e: &Instance) -> Result<Instance, E> {
614    impl_call_float_unary!("exp", e, n => n.exp())
615}
616
617/// `exp2()` builtin function.
618///
619/// Reference: <https://www.w3.org/TR/WGSL/#exp2-builtin>
620pub fn exp2(e: &Instance) -> Result<Instance, E> {
621    impl_call_float_unary!("exp2", e, n => n.exp2())
622}
623
624/// TODO: This built-in is not implemented!
625pub fn extractBits(_a1: &Instance, _a2: &Instance, _a3: &Instance) -> Result<Instance, E> {
626    Err(E::Todo("extractBits".to_string()))
627}
628
629/// TODO: This built-in is not implemented!
630pub fn faceForward(_a1: &Instance, _a2: &Instance, _a3: &Instance) -> Result<Instance, E> {
631    Err(E::Todo("faceForward".to_string()))
632}
633
634/// TODO: This built-in is not implemented!
635pub fn firstLeadingBit(_a1: &Instance) -> Result<Instance, E> {
636    Err(E::Todo("firstLeadingBit".to_string()))
637}
638
639/// TODO: This built-in is not implemented!
640pub fn firstTrailingBit(_a1: &Instance) -> Result<Instance, E> {
641    Err(E::Todo("firstTrailingBit".to_string()))
642}
643
644/// `floor()` builtin function.
645///
646/// Reference: <https://www.w3.org/TR/WGSL/#floor-builtin>
647pub fn floor(e: &Instance) -> Result<Instance, E> {
648    impl_call_float_unary!("floor", e, n => n.floor())
649}
650
651/// TODO: This built-in is not implemented!
652pub fn fma(_a1: &Instance, _a2: &Instance, _a3: &Instance) -> Result<Instance, E> {
653    Err(E::Todo("fma".to_string()))
654}
655
656/// `fract()` builtin function.
657///
658/// Reference: <https://www.w3.org/TR/WGSL/#fract-builtin>
659pub fn fract(e: &Instance, stage: ShaderStage) -> Result<Instance, E> {
660    e.op_sub(&floor(e)?, stage)
661    // impl_call_float_unary!("fract", e, n => n.fract())
662}
663
664/// `frexp()` builtin function.
665///
666/// TODO: This built-in is only partially implemented.
667///
668/// Reference: <https://www.w3.org/TR/WGSL/#frexp-builtin>
669pub fn frexp(e: &Instance) -> Result<Instance, E> {
670    const ERR: E = E::Builtin("`frexp` expects a float or vector of float argument");
671    fn make_frexp_inst(fract: Instance, exp: Instance) -> Instance {
672        Instance::Struct(StructInstance::new(
673            frexp_struct_type(&fract.ty()).unwrap(),
674            vec![fract, exp],
675        ))
676    }
677    // from: https://docs.rs/libm/latest/src/libm/math/frexp.rs.html#1-20
678    fn frexp(x: f64) -> (f64, i32) {
679        let mut y = x.to_bits();
680        let ee = ((y >> 52) & 0x7ff) as i32;
681
682        if ee == 0 {
683            if x != 0.0 {
684                let x1p64 = f64::from_bits(0x43f0000000000000);
685                let (x, e) = frexp(x * x1p64);
686                return (x, e - 64);
687            }
688            return (x, 0);
689        } else if ee == 0x7ff {
690            return (x, 0);
691        }
692
693        let e = ee - 0x3fe;
694        y &= 0x800fffffffffffff;
695        y |= 0x3fe0000000000000;
696        (f64::from_bits(y), e)
697    }
698    match e {
699        Instance::Literal(l) => match l {
700            LiteralInstance::Bool(_) => todo!(),
701            LiteralInstance::AbstractInt(_) => todo!(),
702            LiteralInstance::AbstractFloat(n) => {
703                let (fract, exp) = frexp(*n);
704                Ok(make_frexp_inst(
705                    LiteralInstance::AbstractFloat(fract).into(),
706                    LiteralInstance::AbstractInt(exp as i64).into(),
707                ))
708            }
709            LiteralInstance::I32(_) => todo!(),
710            LiteralInstance::U32(_) => todo!(),
711            LiteralInstance::F32(n) => {
712                let (fract, exp) = frexp(*n as f64);
713                Ok(make_frexp_inst(
714                    LiteralInstance::F32(fract as f32).into(),
715                    LiteralInstance::I32(exp).into(),
716                ))
717            }
718            LiteralInstance::F16(n) => {
719                let (fract, exp) = frexp(n.to_f64().unwrap(/* SAFETY: f16 to f64 is lossless */));
720                Ok(make_frexp_inst(
721                    LiteralInstance::F16(f16::from_f64(fract)).into(),
722                    LiteralInstance::I32(exp).into(),
723                ))
724            }
725            #[cfg(feature = "naga-ext")]
726            LiteralInstance::I64(_) => todo!(),
727            #[cfg(feature = "naga-ext")]
728            LiteralInstance::U64(_) => todo!(),
729            #[cfg(feature = "naga-ext")]
730            LiteralInstance::F64(n) => {
731                let (fract, exp) = frexp(*n);
732                Ok(make_frexp_inst(
733                    LiteralInstance::F64(fract).into(),
734                    LiteralInstance::I64(exp as i64).into(),
735                ))
736            }
737        },
738        Instance::Vec(v) => {
739            let ty = v.inner_ty();
740            let (fracts, exps): (Vec<_>, Vec<_>) = v
741                .iter()
742                .map(|l| match l.unwrap_literal_ref() {
743                    LiteralInstance::AbstractFloat(n) => Ok(*n),
744                    LiteralInstance::F32(n) => Ok(*n as f64),
745                    LiteralInstance::F16(n) => {
746                        Ok(n.to_f64().unwrap(/* SAFETY: f16 to f64 is lossless */))
747                    }
748                    _ => Err(ERR),
749                })
750                .collect::<Result<Vec<_>, _>>()?
751                .into_iter()
752                .map(frexp)
753                .unzip();
754            let fracts = fracts
755                .into_iter()
756                .map(|n| match ty {
757                    Type::AbstractFloat => LiteralInstance::AbstractFloat(n).into(),
758                    Type::F32 => LiteralInstance::F32(n as f32).into(),
759                    Type::F16 => LiteralInstance::F16(f16::from_f64(n)).into(),
760                    _ => unreachable!("case handled above"),
761                })
762                .collect_vec();
763            let exps = exps
764                .into_iter()
765                .map(|n| match ty {
766                    Type::AbstractFloat => LiteralInstance::AbstractInt(n as i64).into(),
767                    Type::F32 => LiteralInstance::I32(n).into(),
768                    Type::F16 => LiteralInstance::I32(n).into(),
769                    _ => unreachable!("case handled above"),
770                })
771                .collect_vec();
772            let fract = VecInstance::new(fracts).into();
773            let exp = VecInstance::new(exps).into();
774            Ok(make_frexp_inst(fract, exp))
775        }
776        _ => Err(ERR),
777    }
778}
779
780/// TODO: This built-in is not implemented!
781pub fn insertBits(
782    _a1: &Instance,
783    _a2: &Instance,
784    _a3: &Instance,
785    _a4: &Instance,
786) -> Result<Instance, E> {
787    Err(E::Todo("insertBits".to_string()))
788}
789
790/// `inverseSqrt()` builtin function.
791///
792// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
793//
794/// Reference: <https://www.w3.org/TR/WGSL/#inverseSqrt-builtin>
795pub fn inverseSqrt(e: &Instance) -> Result<Instance, E> {
796    const ERR: E = E::Builtin("`inverseSqrt` expects a float or vector of float argument");
797    fn lit_isqrt(l: &LiteralInstance) -> Result<LiteralInstance, E> {
798        match l {
799            LiteralInstance::Bool(_) => Err(ERR),
800            LiteralInstance::AbstractInt(_) => l
801                .convert_to(&Type::AbstractFloat)
802                .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))
803                .map(|n| LiteralInstance::from(1.0 / n.unwrap_abstract_float().sqrt())),
804            LiteralInstance::AbstractFloat(n) => Ok(LiteralInstance::from(1.0 / n.sqrt())),
805            LiteralInstance::I32(_) => Err(ERR),
806            LiteralInstance::U32(_) => Err(ERR),
807            LiteralInstance::F32(n) => Ok(LiteralInstance::from(1.0 / n.sqrt())),
808            LiteralInstance::F16(n) => Ok(LiteralInstance::from(f16::one() / n.sqrt())),
809            #[cfg(feature = "naga-ext")]
810            LiteralInstance::I64(_) => Err(ERR),
811            #[cfg(feature = "naga-ext")]
812            LiteralInstance::U64(_) => Err(ERR),
813            #[cfg(feature = "naga-ext")]
814            LiteralInstance::F64(n) => Ok(LiteralInstance::F64(1.0 / n.sqrt())),
815        }
816    }
817    match e {
818        Instance::Literal(l) => lit_isqrt(l).map(Into::into),
819        Instance::Vec(v) => v.compwise_unary(lit_isqrt).map(Into::into),
820        _ => Err(ERR),
821    }
822}
823
824/// `ldexp()` builtin function.
825///
826/// Reference: <https://www.w3.org/TR/WGSL/#ldexp-builtin>
827pub fn ldexp(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
828    // from: https://docs.rs/libm/latest/src/libm/math/scalbn.rs.html#3-34
829    fn scalbn(x: f64, mut n: i32) -> f64 {
830        let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023
831        let x1p53 = f64::from_bits(0x4340000000000000); // 0x1p53 === 2 ^ 53
832        let x1p_1022 = f64::from_bits(0x0010000000000000); // 0x1p-1022 === 2 ^ (-1022)
833
834        let mut y = x;
835
836        if n > 1023 {
837            y *= x1p1023;
838            n -= 1023;
839            if n > 1023 {
840                y *= x1p1023;
841                n -= 1023;
842                if n > 1023 {
843                    n = 1023;
844                }
845            }
846        } else if n < -1022 {
847            /* make sure final n < -53 to avoid double
848            rounding in the subnormal range */
849            y *= x1p_1022 * x1p53;
850            n += 1022 - 53;
851            if n < -1022 {
852                y *= x1p_1022 * x1p53;
853                n += 1022 - 53;
854                if n < -1022 {
855                    n = -1022;
856                }
857            }
858        }
859        y * f64::from_bits(((0x3ff + n) as u64) << 52)
860    }
861    fn ldexp_lit(l1: &LiteralInstance, l2: &LiteralInstance) -> Result<LiteralInstance, E> {
862        match (l1, l2) {
863            (LiteralInstance::AbstractInt(n1), LiteralInstance::AbstractInt(n2)) => Ok(
864                LiteralInstance::AbstractFloat(scalbn(n1.to_f64().unwrap(), n2.to_i32().unwrap())),
865            ),
866            (LiteralInstance::AbstractFloat(n1), LiteralInstance::AbstractInt(n2)) => Ok(
867                LiteralInstance::AbstractFloat(scalbn(*n1, n2.to_i32().unwrap())),
868            ),
869            (LiteralInstance::AbstractInt(n1), LiteralInstance::I32(n2)) => Ok(
870                LiteralInstance::F32(scalbn(n1.to_f64().unwrap(), *n2) as f32),
871            ),
872            (LiteralInstance::AbstractFloat(n1), LiteralInstance::I32(n2)) => Ok(
873                LiteralInstance::F32(scalbn(*n1, n2.to_i32().unwrap()) as f32),
874            ),
875            (LiteralInstance::F32(n1), LiteralInstance::AbstractInt(n2)) => Ok(
876                LiteralInstance::F32(scalbn(n1.to_f64().unwrap(), n2.to_i32().unwrap()) as f32),
877            ),
878            (LiteralInstance::F32(n1), LiteralInstance::I32(n2)) => Ok(LiteralInstance::F32(
879                scalbn(n1.to_f64().unwrap(), n2.to_i32().unwrap()) as f32,
880            )),
881            (LiteralInstance::F16(n1), LiteralInstance::AbstractInt(n2)) => {
882                Ok(LiteralInstance::F16(f16::from_f64(scalbn(
883                    n1.to_f64().unwrap(),
884                    n2.to_i32().unwrap(),
885                ))))
886            }
887            (LiteralInstance::F16(n1), LiteralInstance::I32(n2)) => Ok(LiteralInstance::F16(
888                f16::from_f64(scalbn(n1.to_f64().unwrap(), *n2)),
889            )),
890            _ => Err(E::Builtin(
891                "`ldexp` with scalar arguments expects a float and a i32 arguments",
892            )),
893        }
894    }
895
896    // TODO conversion errors
897    match (e1, e2) {
898        (Instance::Literal(l1), Instance::Literal(l2)) => ldexp_lit(l1, l2).map(Into::into),
899        (Instance::Vec(v1), Instance::Vec(v2)) => v1.compwise_binary(v2, ldexp_lit).map(Into::into),
900        _ => Err(E::Builtin(
901            "`ldexp` expects two scalar or two vector arguments",
902        )),
903    }
904}
905
906/// `length()` builtin function.
907///
908/// Reference: <https://www.w3.org/TR/WGSL/#length-builtin>
909pub fn length(e: &Instance) -> Result<Instance, E> {
910    const ERR: E = E::Builtin("`length` expects a float or vector of float argument");
911    match e {
912        Instance::Literal(_) => abs(e),
913        Instance::Vec(v) => sqrt(
914            &v.op_mul(v, ShaderStage::Exec)?
915                .into_iter()
916                .map(Ok)
917                .reduce(|a, b| a?.op_add(&b?, ShaderStage::Exec))
918                .unwrap()?,
919        ),
920        _ => Err(ERR),
921    }
922}
923
924/// `log()` builtin function.
925///
926/// Reference: <https://www.w3.org/TR/WGSL/#log-builtin>
927pub fn log(e: &Instance) -> Result<Instance, E> {
928    impl_call_float_unary!("log", e, n => n.ln())
929}
930
931/// `log2()` builtin function.
932///
933/// Reference: <https://www.w3.org/TR/WGSL/#log2-builtin>
934pub fn log2(e: &Instance) -> Result<Instance, E> {
935    impl_call_float_unary!("log2", e, n => n.log2())
936}
937
938/// `max()` builtin function.
939///
940/// Reference: <https://www.w3.org/TR/WGSL/#max-builtin>
941pub fn max(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
942    const ERR: E = E::Builtin("`max` expects a scalar or vector of scalar argument");
943    fn lit_max(e1: &LiteralInstance, e2: &LiteralInstance) -> Result<LiteralInstance, E> {
944        match e1 {
945            LiteralInstance::Bool(_) => Err(ERR),
946            LiteralInstance::AbstractInt(e1) => {
947                Ok(LiteralInstance::from(*e1.max(&e2.unwrap_abstract_int())))
948            }
949            LiteralInstance::AbstractFloat(e1) => {
950                Ok(LiteralInstance::from(e1.max(e2.unwrap_abstract_float())))
951            }
952            LiteralInstance::I32(e1) => Ok(LiteralInstance::from(*e1.max(&e2.unwrap_i32()))),
953            LiteralInstance::U32(e1) => Ok(LiteralInstance::from(*e1.max(&e2.unwrap_u32()))),
954            LiteralInstance::F32(e1) => Ok(LiteralInstance::from(e1.max(e2.unwrap_f32()))),
955            LiteralInstance::F16(e1) => Ok(LiteralInstance::from(e1.max(e2.unwrap_f16()))),
956            #[cfg(feature = "naga-ext")]
957            LiteralInstance::I64(e1) => Ok(LiteralInstance::I64(*e1.max(&e2.unwrap_i64()))),
958            #[cfg(feature = "naga-ext")]
959            LiteralInstance::U64(e1) => Ok(LiteralInstance::U64(*e1.max(&e2.unwrap_u64()))),
960            #[cfg(feature = "naga-ext")]
961            LiteralInstance::F64(e1) => Ok(LiteralInstance::F64(e1.max(e2.unwrap_f64()))),
962        }
963    }
964    let (e1, e2) = convert(e1, e2).ok_or(E::Builtin("`max` arguments are incompatible"))?;
965    match (e1, e2) {
966        (Instance::Literal(e1), Instance::Literal(e2)) => lit_max(&e1, &e2).map(Into::into),
967        (Instance::Vec(e1), Instance::Vec(e2)) => e1.compwise_binary(&e2, lit_max).map(Into::into),
968        _ => Err(ERR),
969    }
970}
971
972/// `min()` builtin function.
973///
974/// Reference: <https://www.w3.org/TR/WGSL/#min-builtin>
975pub fn min(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
976    const ERR: E = E::Builtin("`min` expects a scalar or vector of scalar argument");
977    fn lit_min(e1: &LiteralInstance, e2: &LiteralInstance) -> Result<LiteralInstance, E> {
978        match e1 {
979            LiteralInstance::Bool(_) => Err(ERR),
980            LiteralInstance::AbstractInt(e1) => {
981                Ok(LiteralInstance::from(*e1.min(&e2.unwrap_abstract_int())))
982            }
983            LiteralInstance::AbstractFloat(e1) => {
984                Ok(LiteralInstance::from(e1.min(e2.unwrap_abstract_float())))
985            }
986            LiteralInstance::I32(e1) => Ok(LiteralInstance::from(*e1.min(&e2.unwrap_i32()))),
987            LiteralInstance::U32(e1) => Ok(LiteralInstance::from(*e1.min(&e2.unwrap_u32()))),
988            LiteralInstance::F32(e1) => Ok(LiteralInstance::from(e1.min(e2.unwrap_f32()))),
989            LiteralInstance::F16(e1) => Ok(LiteralInstance::from(e1.min(e2.unwrap_f16()))),
990            #[cfg(feature = "naga-ext")]
991            LiteralInstance::I64(e1) => Ok(LiteralInstance::I64(*e1.max(&e2.unwrap_i64()))),
992            #[cfg(feature = "naga-ext")]
993            LiteralInstance::U64(e1) => Ok(LiteralInstance::U64(*e1.max(&e2.unwrap_u64()))),
994            #[cfg(feature = "naga-ext")]
995            LiteralInstance::F64(e1) => Ok(LiteralInstance::F64(e1.max(e2.unwrap_f64()))),
996        }
997    }
998    let (e1, e2) = convert(e1, e2).ok_or(E::Builtin("`min` arguments are incompatible"))?;
999    match (e1, e2) {
1000        (Instance::Literal(e1), Instance::Literal(e2)) => lit_min(&e1, &e2).map(Into::into),
1001        (Instance::Vec(e1), Instance::Vec(e2)) => e1.compwise_binary(&e2, lit_min).map(Into::into),
1002        _ => Err(ERR),
1003    }
1004}
1005
1006/// `mix()` builtin function.
1007///
1008/// Reference: <https://www.w3.org/TR/WGSL/#mix-builtin>
1009pub fn mix(e1: &Instance, e2: &Instance, e3: &Instance, stage: ShaderStage) -> Result<Instance, E> {
1010    let tys = [e1.inner_ty(), e2.inner_ty(), e3.inner_ty()];
1011    let inner_ty = convert_all_ty(&tys).ok_or(E::Builtin("`mix` arguments are incompatible"))?;
1012    let e1 = e1.convert_inner_to(inner_ty).unwrap();
1013    let e2 = e2.convert_inner_to(inner_ty).unwrap();
1014    let e3 = e3.convert_inner_to(inner_ty).unwrap();
1015    let (e1, e2) = convert(&e1, &e2).ok_or(E::Builtin("`mix` arguments are incompatible"))?;
1016
1017    // TODO is it ok with abstract int? it's supposed to be of type inner_ty
1018    let one = Instance::Literal(LiteralInstance::AbstractInt(1));
1019
1020    e1.op_mul(&one.op_sub(&e3, stage)?, stage)?
1021        .op_add(&e2.op_mul(&e3, stage)?, stage)
1022}
1023
1024/// TODO: This built-in is not implemented!
1025pub fn modf(_a1: &Instance) -> Result<Instance, E> {
1026    Err(E::Todo("modf".to_string()))
1027}
1028
1029/// `normalize()` builtin function.
1030///
1031/// Reference: <https://www.w3.org/TR/WGSL/#normalize-builtin>
1032pub fn normalize(e: &Instance, stage: ShaderStage) -> Result<Instance, E> {
1033    e.op_div(&length(e)?, stage)
1034}
1035
1036/// `pow()` builtin function.
1037///
1038/// Reference: <https://www.w3.org/TR/WGSL/#pow-builtin>
1039pub fn pow(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1040    const ERR: E = E::Builtin("`pow` expects a scalar or vector of scalar argument");
1041    fn lit_powf(e1: &LiteralInstance, e2: &LiteralInstance) -> Result<LiteralInstance, E> {
1042        match e1 {
1043            LiteralInstance::Bool(_) => Err(ERR),
1044            LiteralInstance::AbstractInt(_) => {
1045                let e1 = e1
1046                    .convert_to(&Type::AbstractFloat)
1047                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?
1048                    .unwrap_abstract_float();
1049                let e2 = e2
1050                    .convert_to(&Type::AbstractFloat)
1051                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?
1052                    .unwrap_abstract_float();
1053                Ok(LiteralInstance::from(e1.powf(e2)))
1054            }
1055            LiteralInstance::AbstractFloat(e1) => {
1056                Ok(LiteralInstance::from(e1.powf(e2.unwrap_abstract_float())))
1057            }
1058            LiteralInstance::I32(_) => Err(ERR),
1059            LiteralInstance::U32(_) => Err(ERR),
1060            LiteralInstance::F32(e1) => Ok(LiteralInstance::from(e1.powf(e2.unwrap_f32()))),
1061            LiteralInstance::F16(e1) => Ok(LiteralInstance::from(e1.powf(e2.unwrap_f16()))),
1062            #[cfg(feature = "naga-ext")]
1063            LiteralInstance::I64(_) => Err(ERR),
1064            #[cfg(feature = "naga-ext")]
1065            LiteralInstance::U64(_) => Err(ERR),
1066            #[cfg(feature = "naga-ext")]
1067            LiteralInstance::F64(e1) => Ok(LiteralInstance::F64(e1.powf(e2.unwrap_f64()))),
1068        }
1069    }
1070    let (e1, e2) = convert(e1, e2).ok_or(E::Builtin("`pow` arguments are incompatible"))?;
1071    match (e1, e2) {
1072        (Instance::Literal(e1), Instance::Literal(e2)) => lit_powf(&e1, &e2).map(Into::into),
1073        (Instance::Vec(e1), Instance::Vec(e2)) => e1.compwise_binary(&e2, lit_powf).map(Into::into),
1074        _ => Err(ERR),
1075    }
1076}
1077
1078/// TODO: This built-in is not implemented!
1079pub fn quantizeToF16(_a1: &Instance) -> Result<Instance, E> {
1080    Err(E::Todo("quantizeToF16".to_string()))
1081}
1082
1083/// `radians()` builtin function.
1084///
1085/// Reference: <https://www.w3.org/TR/WGSL/#radians-builtin>
1086pub fn radians(e: &Instance) -> Result<Instance, E> {
1087    impl_call_float_unary!("radians", e, n => n.to_radians())
1088}
1089
1090/// TODO: This built-in is not implemented!
1091pub fn reflect(_a1: &Instance, _a2: &Instance) -> Result<Instance, E> {
1092    Err(E::Todo("reflect".to_string()))
1093}
1094
1095/// TODO: This built-in is not implemented!
1096pub fn refract(_a1: &Instance, _a2: &Instance, _a3: &Instance) -> Result<Instance, E> {
1097    Err(E::Todo("refract".to_string()))
1098}
1099
1100/// TODO: This built-in is not implemented!
1101pub fn reverseBits(_a1: &Instance) -> Result<Instance, E> {
1102    Err(E::Todo("reverseBits".to_string()))
1103}
1104
1105/// `round()` builtin function.
1106///
1107/// Reference: <https://www.w3.org/TR/WGSL/#round-builtin>
1108pub fn round(e: &Instance) -> Result<Instance, E> {
1109    const ERR: E = E::Builtin("`round` expects a float or vector of float argument");
1110    fn lit_fn(l: &LiteralInstance) -> Result<LiteralInstance, E> {
1111        match l {
1112            LiteralInstance::Bool(_) => Err(ERR),
1113            LiteralInstance::AbstractInt(_) => {
1114                let n = l
1115                    .convert_to(&Type::AbstractFloat)
1116                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?
1117                    .unwrap_abstract_float();
1118                Ok(LiteralInstance::from(n.round_ties_even()))
1119            }
1120            LiteralInstance::AbstractFloat(n) => Ok(LiteralInstance::from(n.round_ties_even())),
1121            LiteralInstance::I32(_) => Err(ERR),
1122            LiteralInstance::U32(_) => Err(ERR),
1123            LiteralInstance::F32(n) => Ok(LiteralInstance::from(n.round_ties_even())),
1124            LiteralInstance::F16(n) => Ok(LiteralInstance::from(f16::from_f32(
1125                f16::to_f32(*n).round_ties_even(),
1126            ))),
1127            #[cfg(feature = "naga-ext")]
1128            LiteralInstance::I64(_) => Err(ERR),
1129            #[cfg(feature = "naga-ext")]
1130            LiteralInstance::U64(_) => Err(ERR),
1131            #[cfg(feature = "naga-ext")]
1132            LiteralInstance::F64(n) => Ok(LiteralInstance::F64(n.round_ties_even())),
1133        }
1134    }
1135    match e {
1136        Instance::Literal(l) => lit_fn(l).map(Into::into),
1137        Instance::Vec(v) => v.compwise_unary(lit_fn).map(Into::into),
1138        _ => Err(ERR),
1139    }
1140}
1141
1142/// `saturate()` builtin function.
1143///
1144/// Reference: <https://www.w3.org/TR/WGSL/#saturate-builtin>
1145pub fn saturate(e: &Instance) -> Result<Instance, E> {
1146    match e {
1147        Instance::Literal(_) => {
1148            let zero = LiteralInstance::AbstractFloat(0.0);
1149            let one = LiteralInstance::AbstractFloat(1.0);
1150            clamp(e, &zero.into(), &one.into())
1151        }
1152        Instance::Vec(v) => {
1153            let n = v.n();
1154            let zero = Instance::from(LiteralInstance::AbstractFloat(0.0));
1155            let one = Instance::from(LiteralInstance::AbstractFloat(1.0));
1156            let zero = VecInstance::new((0..n).map(|_| zero.clone()).collect_vec());
1157            let one = VecInstance::new((0..n).map(|_| one.clone()).collect_vec());
1158            clamp(e, &zero.into(), &one.into())
1159        }
1160        _ => Err(E::Builtin(
1161            "`saturate` expects a float or vector of float argument",
1162        )),
1163    }
1164}
1165
1166/// `sign()` builtin function.
1167///
1168/// Reference: <https://www.w3.org/TR/WGSL/#sign-builtin>
1169pub fn sign(e: &Instance) -> Result<Instance, E> {
1170    const ERR: E = E::Builtin(concat!(
1171        "`",
1172        "sign",
1173        "` expects a float or vector of float argument"
1174    ));
1175    fn lit_fn(l: &LiteralInstance) -> Result<LiteralInstance, E> {
1176        match l {
1177            LiteralInstance::Bool(_) => Err(ERR),
1178            LiteralInstance::AbstractInt(n) => Ok(LiteralInstance::from(n.signum())),
1179            LiteralInstance::AbstractFloat(n) => Ok(LiteralInstance::from(if n.is_zero() {
1180                *n
1181            } else {
1182                n.signum()
1183            })),
1184            LiteralInstance::I32(n) => Ok(LiteralInstance::from(n.signum())),
1185            LiteralInstance::U32(n) => Ok(LiteralInstance::from(if n.is_zero() {
1186                *n
1187            } else {
1188                1
1189            })),
1190            LiteralInstance::F32(n) => Ok(LiteralInstance::from(if n.is_zero() {
1191                *n
1192            } else {
1193                n.signum()
1194            })),
1195            LiteralInstance::F16(n) => Ok(LiteralInstance::from(if n.is_zero() {
1196                *n
1197            } else {
1198                n.signum()
1199            })),
1200            #[cfg(feature = "naga-ext")]
1201            LiteralInstance::I64(n) => Ok(LiteralInstance::I64(n.signum())),
1202            #[cfg(feature = "naga-ext")]
1203            LiteralInstance::U64(n) => Ok(LiteralInstance::U64(if n.is_zero() {
1204                *n
1205            } else {
1206                1
1207            })),
1208            #[cfg(feature = "naga-ext")]
1209            LiteralInstance::F64(n) => Ok(LiteralInstance::F64(if n.is_zero() {
1210                *n
1211            } else {
1212                n.signum()
1213            })),
1214        }
1215    }
1216    match e {
1217        Instance::Literal(l) => lit_fn(l).map(Into::into),
1218        Instance::Vec(v) => v.compwise_unary(lit_fn).map(Into::into),
1219        _ => Err(ERR),
1220    }
1221}
1222
1223/// `sin()` builtin function.
1224///
1225/// Reference: <https://www.w3.org/TR/WGSL/#sin-builtin>
1226pub fn sin(e: &Instance) -> Result<Instance, E> {
1227    impl_call_float_unary!("sin", e, n => n.sin())
1228}
1229
1230/// `sinh()` builtin function.
1231///
1232/// Reference: <https://www.w3.org/TR/WGSL/#sinh-builtin>
1233pub fn sinh(e: &Instance) -> Result<Instance, E> {
1234    impl_call_float_unary!("sinh", e, n => n.sinh())
1235}
1236
1237/// TODO: This built-in is not implemented!
1238pub fn smoothstep(_low: &Instance, _high: &Instance, _x: &Instance) -> Result<Instance, E> {
1239    Err(E::Todo("smoothstep".to_string()))
1240}
1241
1242/// `sqrt()` builtin function.
1243///
1244/// Reference: <https://www.w3.org/TR/WGSL/#sqrt-builtin>
1245pub fn sqrt(e: &Instance) -> Result<Instance, E> {
1246    impl_call_float_unary!("sqrt", e, n => n.sqrt())
1247}
1248
1249/// `step()` builtin function.
1250///
1251/// Reference: <https://www.w3.org/TR/WGSL/#step-builtin>
1252pub fn step(edge: &Instance, x: &Instance) -> Result<Instance, E> {
1253    const ERR: E = E::Builtin("`step` expects a float or vector of float argument");
1254    fn lit_step(edge: &LiteralInstance, x: &LiteralInstance) -> Result<LiteralInstance, E> {
1255        match edge {
1256            LiteralInstance::Bool(_) => Err(ERR),
1257            LiteralInstance::AbstractInt(_) => {
1258                let edge = edge
1259                    .convert_to(&Type::AbstractFloat)
1260                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?
1261                    .unwrap_abstract_float();
1262                let x = x
1263                    .convert_to(&Type::AbstractFloat)
1264                    .ok_or(E::Conversion(Type::AbstractInt, Type::AbstractFloat))?
1265                    .unwrap_abstract_float();
1266                Ok(LiteralInstance::from(if edge <= x {
1267                    1.0
1268                } else {
1269                    0.0
1270                }))
1271            }
1272            LiteralInstance::AbstractFloat(edge) => Ok(LiteralInstance::from(
1273                if *edge <= x.unwrap_abstract_float() {
1274                    1.0
1275                } else {
1276                    0.0
1277                },
1278            )),
1279            LiteralInstance::I32(_) => Err(ERR),
1280            LiteralInstance::U32(_) => Err(ERR),
1281            LiteralInstance::F32(edge) => Ok(LiteralInstance::from(if *edge <= x.unwrap_f32() {
1282                1.0
1283            } else {
1284                0.0
1285            })),
1286            LiteralInstance::F16(edge) => Ok(LiteralInstance::from(if *edge <= x.unwrap_f16() {
1287                1.0
1288            } else {
1289                0.0
1290            })),
1291            #[cfg(feature = "naga-ext")]
1292            LiteralInstance::I64(_) => Err(ERR),
1293            #[cfg(feature = "naga-ext")]
1294            LiteralInstance::U64(_) => Err(ERR),
1295            #[cfg(feature = "naga-ext")]
1296            LiteralInstance::F64(edge) => Ok(LiteralInstance::F64(if *edge <= x.unwrap_f64() {
1297                1.0
1298            } else {
1299                0.0
1300            })),
1301        }
1302    }
1303    let (edge, x) = convert(edge, x).ok_or(E::Builtin("`step` arguments are incompatible"))?;
1304    match (edge, x) {
1305        (Instance::Literal(edge), Instance::Literal(x)) => lit_step(&edge, &x).map(Into::into),
1306        (Instance::Vec(edge), Instance::Vec(x)) => {
1307            edge.compwise_binary(&x, lit_step).map(Into::into)
1308        }
1309        _ => Err(ERR),
1310    }
1311}
1312
1313/// `tan()` builtin function.
1314///
1315/// Reference: <https://www.w3.org/TR/WGSL/#tan-builtin>
1316pub fn tan(e: &Instance) -> Result<Instance, E> {
1317    impl_call_float_unary!("tan", e, n => n.tan())
1318}
1319
1320/// `tanh()` builtin function.
1321///
1322/// Reference: <https://www.w3.org/TR/WGSL/#tanh-builtin>
1323pub fn tanh(e: &Instance) -> Result<Instance, E> {
1324    impl_call_float_unary!("tanh", e, n => n.tanh())
1325}
1326
1327/// `transpose()` builtin function.
1328///
1329/// Reference: <https://www.w3.org/TR/WGSL/#transpose-builtin>
1330pub fn transpose(e: &Instance) -> Result<Instance, E> {
1331    match e {
1332        Instance::Mat(e) => Ok(e.transpose().into()),
1333        _ => Err(E::Builtin("`transpose` expects a matrix argument")),
1334    }
1335}
1336
1337/// `trunc()` builtin function.
1338///
1339/// Reference: <https://www.w3.org/TR/WGSL/#trunc-builtin>
1340pub fn trunc(e: &Instance) -> Result<Instance, E> {
1341    impl_call_float_unary!("trunc", e, n => n.trunc())
1342}
1343
1344// ------
1345// ATOMIC
1346// ------
1347// reference: <https://www.w3.org/TR/WGSL/#atomic-builtin-functions>
1348
1349/// `atomicLoad()` builtin function.
1350///
1351/// Reference: <https://www.w3.org/TR/WGSL/#atomicLoad-builtin>
1352pub fn atomicLoad(e: &Instance) -> Result<Instance, E> {
1353    let err = E::Builtin("`atomicLoad` expects a pointer to atomic argument");
1354    if let Instance::Ptr(ptr) = e {
1355        // TODO: there is a ptr.ptr.ptr chain here. Rename it.
1356        let inst = ptr.ptr.read()?;
1357        if let Instance::Atomic(inst) = &*inst {
1358            Ok(inst.inner().clone())
1359        } else {
1360            Err(err)
1361        }
1362    } else {
1363        Err(err)
1364    }
1365}
1366
1367/// `atomicStore()` builtin function.
1368///
1369/// Reference: <https://www.w3.org/TR/WGSL/#atomicStore-builtin>
1370pub fn atomicStore(e1: &Instance, e2: &Instance) -> Result<(), E> {
1371    let err = E::Builtin("`atomicStore` expects a pointer to atomic argument");
1372    if let Instance::Ptr(ptr) = e1 {
1373        let mut inst = ptr.ptr.read_write()?;
1374        if let Instance::Atomic(inst) = &mut *inst {
1375            let ty = inst.inner().ty();
1376            let e2 = e2
1377                .convert_to(&ty)
1378                .ok_or_else(|| E::ParamType(ty, e2.ty()))?;
1379            *inst = AtomicInstance::new(e2);
1380            Ok(())
1381        } else {
1382            Err(err)
1383        }
1384    } else {
1385        Err(err)
1386    }
1387}
1388
1389/// `atomicSub()` builtin function.
1390///
1391/// Reference: <https://www.w3.org/TR/WGSL/#atomicSub-builtin>
1392pub fn atomicSub(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1393    let initial = atomicLoad(e1)?;
1394    atomicStore(e1, &initial.op_sub(e2, ShaderStage::Exec)?)?;
1395    Ok(initial)
1396}
1397
1398/// `atomicMax()` builtin function.
1399///
1400/// Reference: <https://www.w3.org/TR/WGSL/#atomicMax-builtin>
1401pub fn atomicMax(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1402    let initial = atomicLoad(e1)?;
1403    atomicStore(e1, &max(&initial, e2)?)?;
1404    Ok(initial)
1405}
1406
1407/// `atomicMin()` builtin function.
1408///
1409/// Reference: <https://www.w3.org/TR/WGSL/#atomicMin-builtin>
1410pub fn atomicMin(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1411    let initial = atomicLoad(e1)?;
1412    atomicStore(e1, &max(&initial, e2)?)?;
1413    Ok(initial)
1414}
1415
1416/// `atomicAnd()` builtin function.
1417///
1418/// Reference: <https://www.w3.org/TR/WGSL/#atomicAnd-builtin>
1419pub fn atomicAnd(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1420    let initial = atomicLoad(e1)?;
1421    atomicStore(e1, &initial.op_bitand(e2)?)?;
1422    Ok(initial)
1423}
1424
1425/// `atomicOr()` builtin function.
1426///
1427/// Reference: <https://www.w3.org/TR/WGSL/#atomicOr-builtin>
1428pub fn atomicOr(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1429    let initial = atomicLoad(e1)?;
1430    atomicStore(e1, &initial.op_bitor(e2)?)?;
1431    Ok(initial)
1432}
1433
1434/// `atomicXor()` builtin function.
1435///
1436/// Reference: <https://www.w3.org/TR/WGSL/#atomicXor-builtin>
1437pub fn atomicXor(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1438    let initial = atomicLoad(e1)?;
1439    atomicStore(e1, &initial.op_bitxor(e2)?)?;
1440    Ok(initial)
1441}
1442
1443/// `atomicExchange()` builtin function.
1444///
1445/// Reference: <https://www.w3.org/TR/WGSL/#atomicExchange-builtin>
1446pub fn atomicExchange(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1447    let initial = atomicLoad(e1)?;
1448    atomicStore(e1, e2)?;
1449    Ok(initial)
1450}
1451
1452/// `atomicCompareExchangeWeak()` builtin function.
1453///
1454/// Reference: <https://www.w3.org/TR/WGSL/#atomicCompareExchangeWeak-builtin>
1455pub fn atomicCompareExchangeWeak(e1: &Instance, e2: &Instance) -> Result<Instance, E> {
1456    let initial = atomicLoad(e1)?;
1457    let exchanged = if initial == *e2 {
1458        false
1459    } else {
1460        atomicStore(e1, e2)?;
1461        true
1462    };
1463    Ok(Instance::Struct(StructInstance::new(
1464        atomic_compare_exchange_struct_type(&initial.ty()),
1465        vec![initial, LiteralInstance::Bool(exchanged).into()],
1466    )))
1467}
1468
1469// ------------
1470// DATA PACKING
1471// ------------
1472// reference: <https://www.w3.org/TR/WGSL/#pack-builtin-functions>
1473
1474/// `pack4x8snorm()` builtin function.
1475///
1476/// Reference: <https://www.w3.org/TR/WGSL/#pack4x8snorm-builtin>
1477pub fn pack4x8snorm(e: &Instance) -> Result<Instance, E> {
1478    const ERR: E = E::Builtin("`pack4x8snorm` expects a `vec4<f32>` argument");
1479
1480    let v = e
1481        .convert_to(&Type::Vec(4, Type::F32.into()))
1482        .ok_or(ERR)?
1483        .unwrap_vec();
1484
1485    let mut result = 0u32;
1486    for i in 0..4 {
1487        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_f32();
1488        let bits = (0.5 + 127.0 * val.clamp(-1.0, 1.0)).floor() as i8 as u8;
1489        result |= (bits as u32) << (8 * i);
1490    }
1491    Ok(LiteralInstance::U32(result).into())
1492}
1493
1494/// `pack4x8unorm()` builtin function.
1495///
1496/// Reference: <https://www.w3.org/TR/WGSL/#pack4x8unorm-builtin>
1497pub fn pack4x8unorm(e: &Instance) -> Result<Instance, E> {
1498    const ERR: E = E::Builtin("`pack4x8unorm` expects a `vec4<f32>` argument");
1499
1500    let v = e
1501        .convert_to(&Type::Vec(4, Type::F32.into()))
1502        .ok_or(ERR)?
1503        .unwrap_vec();
1504
1505    let mut result = 0u32;
1506    for i in 0..4 {
1507        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_f32();
1508        let bits = (0.5 + 255.0 * val.clamp(0.0, 1.0)).floor() as u8;
1509        result |= (bits as u32) << (8 * i);
1510    }
1511    Ok(LiteralInstance::U32(result).into())
1512}
1513
1514/// `pack4xI8()` builtin function.
1515///
1516/// Reference: <https://www.w3.org/TR/WGSL/#pack4xI8-builtin>
1517pub fn pack4xI8(e: &Instance) -> Result<Instance, E> {
1518    const ERR: E = E::Builtin("`pack4xI8` expects a `vec4<i32>` argument");
1519
1520    let v = e
1521        .convert_to(&Type::Vec(4, Type::I32.into()))
1522        .ok_or(ERR)?
1523        .unwrap_vec();
1524
1525    let mut result = 0u32;
1526    for i in 0..4 {
1527        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_i32();
1528        result |= (val as u8 as u32) << (8 * i);
1529    }
1530    Ok(LiteralInstance::U32(result).into())
1531}
1532
1533/// `pack4xU8()` builtin function.
1534///
1535/// Reference: <https://www.w3.org/TR/WGSL/#pack4xU8-builtin>
1536pub fn pack4xU8(e: &Instance) -> Result<Instance, E> {
1537    const ERR: E = E::Builtin("`pack4xU8` expects a `vec4<u32>` argument");
1538
1539    let v = e
1540        .convert_to(&Type::Vec(4, Type::U32.into()))
1541        .ok_or(ERR)?
1542        .unwrap_vec();
1543
1544    let mut result = 0u32;
1545    for i in 0..4 {
1546        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_u32();
1547        result |= (val as u8 as u32) << (8 * i);
1548    }
1549    Ok(LiteralInstance::U32(result).into())
1550}
1551
1552/// `pack4xI8Clamp()` builtin function.
1553///
1554/// Reference: <https://www.w3.org/TR/WGSL/#pack4xI8Clamp-builtin>
1555pub fn pack4xI8Clamp(e: &Instance) -> Result<Instance, E> {
1556    const ERR: E = E::Builtin("`pack4xI8Clamp` expects a `vec4<i32>` argument");
1557
1558    let v = e
1559        .convert_to(&Type::Vec(4, Type::I32.into()))
1560        .ok_or(ERR)?
1561        .unwrap_vec();
1562
1563    let mut result = 0u32;
1564    for i in 0..4 {
1565        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_i32();
1566        result |= (val as i8 as u32) << (8 * i);
1567    }
1568    Ok(LiteralInstance::U32(result).into())
1569}
1570
1571/// `pack4xU8Clamp()` builtin function.
1572///
1573/// Reference: <https://www.w3.org/TR/WGSL/#pack4xU8Clamp-builtin>
1574pub fn pack4xU8Clamp(e: &Instance) -> Result<Instance, E> {
1575    const ERR: E = E::Builtin("`pack4xU8Clamp` expects a `vec4<u32>` argument");
1576
1577    let v = e
1578        .convert_to(&Type::Vec(4, Type::U32.into()))
1579        .ok_or(ERR)?
1580        .unwrap_vec();
1581
1582    let mut result = 0u32;
1583    for i in 0..4 {
1584        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_u32();
1585        result |= (val as u8 as u32) << (8 * i);
1586    }
1587    Ok(LiteralInstance::U32(result).into())
1588}
1589
1590/// `pack2x16snorm()` builtin function.
1591///
1592/// Reference: <https://www.w3.org/TR/WGSL/#pack2x16snorm-builtin>
1593pub fn pack2x16snorm(e: &Instance) -> Result<Instance, E> {
1594    const ERR: E = E::Builtin("`pack2x16snorm` expects a `vec2<f32>` argument");
1595
1596    let v = e
1597        .convert_to(&Type::Vec(2, Type::F32.into()))
1598        .ok_or(ERR)?
1599        .unwrap_vec();
1600
1601    let mut result = 0u32;
1602    for i in 0..2 {
1603        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_f32();
1604        let bits = (0.5 + 32767.0 * val.clamp(-1.0, 1.0)).floor() as i16 as u16;
1605        result |= (bits as u32) << (16 * i);
1606    }
1607    Ok(LiteralInstance::U32(result).into())
1608}
1609
1610/// `pack2x16unorm()` builtin function.
1611///
1612/// Reference: <https://www.w3.org/TR/WGSL/#pack2x16unorm-builtin>
1613pub fn pack2x16unorm(e: &Instance) -> Result<Instance, E> {
1614    const ERR: E = E::Builtin("`pack2x16unorm` expects a `vec2<f32>` argument");
1615
1616    let v = e
1617        .convert_to(&Type::Vec(2, Type::F32.into()))
1618        .ok_or(ERR)?
1619        .unwrap_vec();
1620
1621    let mut result = 0u32;
1622    for i in 0..2 {
1623        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_f32();
1624        let bits = (0.5 + 65535.0 * val.clamp(0.0, 1.0)).floor() as u16;
1625        result |= (bits as u32) << (16 * i);
1626    }
1627    Ok(LiteralInstance::U32(result).into())
1628}
1629
1630/// `pack2x16float()` builtin function.
1631///
1632/// Reference: <https://www.w3.org/TR/WGSL/#pack2x16float-builtin>
1633pub fn pack2x16float(e: &Instance) -> Result<Instance, E> {
1634    const ERR: E = E::Builtin("`pack2x16float` expects a `vec2<f32>` argument");
1635
1636    let v = e
1637        .convert_to(&Type::Vec(2, Type::F32.into()))
1638        .ok_or(ERR)?
1639        .unwrap_vec();
1640
1641    let mut result = 0u32;
1642    for i in 0..2 {
1643        let val = v.get(i).unwrap().unwrap_literal_ref().unwrap_f32();
1644        let bits = f16::from_f32(val).to_bits();
1645        result |= (bits as u32) << (16 * i);
1646    }
1647    Ok(LiteralInstance::U32(result).into())
1648}
1649
1650/// `unpack4x8snorm()` builtin function.
1651///
1652/// Reference: <https://www.w3.org/TR/WGSL/#unpack4x8snorm-builtin>
1653pub fn unpack4x8snorm(e: &Instance) -> Result<Instance, E> {
1654    const ERR: E = E::Builtin("`unpack4x8snorm` expects a `u32` argument");
1655
1656    let e = e
1657        .convert_to(&Type::U32)
1658        .ok_or(ERR)?
1659        .unwrap_literal()
1660        .unwrap_u32();
1661
1662    let comps = e
1663        .to_le_bytes()
1664        .map(|c| ((c as i8 as f32) / 127.0).max(-1.0))
1665        .map(Instance::from)
1666        .to_vec();
1667
1668    Ok(VecInstance::new(comps).into())
1669}
1670
1671/// `unpack4x8unorm()` builtin function.
1672///
1673/// Reference: <https://www.w3.org/TR/WGSL/#unpack4x8unorm-builtin>
1674pub fn unpack4x8unorm(e: &Instance) -> Result<Instance, E> {
1675    const ERR: E = E::Builtin("`unpack4x8unorm` expects a `u32` argument");
1676
1677    let e = e
1678        .convert_to(&Type::U32)
1679        .ok_or(ERR)?
1680        .unwrap_literal()
1681        .unwrap_u32();
1682
1683    let comps = e
1684        .to_le_bytes()
1685        .map(|c| (c as f32) / 255.0)
1686        .map(Instance::from)
1687        .to_vec();
1688
1689    Ok(VecInstance::new(comps).into())
1690}
1691
1692/// `unpack4xI8()` builtin function.
1693///
1694/// Reference: <https://www.w3.org/TR/WGSL/#unpack4xI8-builtin>
1695pub fn unpack4xI8(e: &Instance) -> Result<Instance, E> {
1696    const ERR: E = E::Builtin("`unpack4xI8` expects a `u32` argument");
1697
1698    let e = e
1699        .convert_to(&Type::U32)
1700        .ok_or(ERR)?
1701        .unwrap_literal()
1702        .unwrap_u32();
1703
1704    let comps = e
1705        .to_le_bytes()
1706        .map(|c| c as i8 as i32)
1707        .map(Instance::from)
1708        .to_vec();
1709
1710    Ok(VecInstance::new(comps).into())
1711}
1712
1713/// `unpack4xU8()` builtin function.
1714///
1715/// Reference: <https://www.w3.org/TR/WGSL/#unpack4xU8-builtin>
1716pub fn unpack4xU8(e: &Instance) -> Result<Instance, E> {
1717    const ERR: E = E::Builtin("`unpack4xU8` expects a `u32` argument");
1718
1719    let e = e
1720        .convert_to(&Type::U32)
1721        .ok_or(ERR)?
1722        .unwrap_literal()
1723        .unwrap_u32();
1724
1725    let comps = e
1726        .to_le_bytes()
1727        .map(|c| c as u32)
1728        .map(Instance::from)
1729        .to_vec();
1730
1731    Ok(VecInstance::new(comps).into())
1732}
1733
1734/// `unpack2x16snorm()` builtin function.
1735///
1736/// Reference: <https://www.w3.org/TR/WGSL/#unpack2x16snorm-builtin>
1737pub fn unpack2x16snorm(e: &Instance) -> Result<Instance, E> {
1738    const ERR: E = E::Builtin("`unpack2x16snorm` expects a `u32` argument");
1739
1740    let e = e
1741        .convert_to(&Type::U32)
1742        .ok_or(ERR)?
1743        .unwrap_literal()
1744        .unwrap_u32();
1745
1746    let lsb = e as u16 as i16;
1747    let msb = (e >> 16) as u16 as i16;
1748
1749    let comps = [lsb, msb]
1750        .map(|c| ((c as f32) / 32767.0).max(-1.0))
1751        .map(Instance::from)
1752        .to_vec();
1753
1754    Ok(VecInstance::new(comps).into())
1755}
1756
1757/// `unpack2x16unorm()` builtin function.
1758///
1759/// Reference: <https://www.w3.org/TR/WGSL/#unpack2x16unorm-builtin>
1760pub fn unpack2x16unorm(e: &Instance) -> Result<Instance, E> {
1761    const ERR: E = E::Builtin("`unpack2x16unorm` expects a `u32` argument");
1762
1763    let e = e
1764        .convert_to(&Type::U32)
1765        .ok_or(ERR)?
1766        .unwrap_literal()
1767        .unwrap_u32();
1768
1769    let lsb = e as u16;
1770    let msb = (e >> 16) as u16;
1771
1772    let comps = [lsb, msb]
1773        .map(|c| (c as f32) / 65535.0)
1774        .map(Instance::from)
1775        .to_vec();
1776
1777    Ok(VecInstance::new(comps).into())
1778}
1779
1780/// `unpack2x16float()` builtin function.
1781///
1782/// Reference: <https://www.w3.org/TR/WGSL/#unpack2x16float-builtin>
1783pub fn unpack2x16float(e: &Instance) -> Result<Instance, E> {
1784    const ERR: E = E::Builtin("`unpack2x16float` expects a `u32` argument");
1785
1786    let e = e
1787        .convert_to(&Type::U32)
1788        .ok_or(ERR)?
1789        .unwrap_literal()
1790        .unwrap_u32();
1791
1792    let lsb = e as u16;
1793    let msb = (e >> 16) as u16;
1794
1795    let comps = [lsb, msb]
1796        .map(|c| f16::from_bits(c).to_f32())
1797        .map(Instance::from)
1798        .to_vec();
1799
1800    Ok(VecInstance::new(comps).into())
1801}
1802
1803impl VecInstance {
1804    /// Warning, this function does not check operand types
1805    pub fn dot(&self, rhs: &VecInstance, stage: ShaderStage) -> Result<LiteralInstance, E> {
1806        self.compwise_binary(rhs, |a, b| a.op_mul(b, stage))?
1807            .into_iter()
1808            .map(|c| Ok(c.unwrap_literal()))
1809            .reduce(|a, b| a?.op_add(&b?, stage))
1810            .unwrap()
1811    }
1812}
1813
1814impl MatInstance {
1815    /// Warning, this function does not check operand types
1816    pub fn transpose(&self) -> MatInstance {
1817        let components = (0..self.r())
1818            .map(|j| {
1819                VecInstance::new(
1820                    (0..self.c())
1821                        .map(|i| self.get(i, j).unwrap().clone())
1822                        .collect_vec(),
1823                )
1824                .into()
1825            })
1826            .collect_vec();
1827        MatInstance::from_cols(components)
1828    }
1829}