Skip to main content

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