naga/back/
pipeline_constants.rs

1use alloc::{
2    borrow::Cow,
3    boxed::Box,
4    string::{String, ToString},
5    vec::Vec,
6};
7use core::mem;
8
9use hashbrown::HashSet;
10use thiserror::Error;
11
12use super::PipelineConstants;
13use crate::{
14    arena::HandleVec,
15    compact::{compact, KeepUnused},
16    ir,
17    proc::{ConstantEvaluator, ConstantEvaluatorError, Emitter},
18    valid::{Capabilities, ModuleInfo, ValidationError, ValidationFlags, Validator},
19    Arena, Block, Constant, Expression, Function, Handle, Literal, Module, Override, Range, Scalar,
20    Span, Statement, TypeInner, WithSpan,
21};
22
23// Possibly unused if not compiled with no_std
24#[allow(unused_imports)]
25use num_traits::float::FloatCore as _;
26
27#[derive(Error, Debug, Clone)]
28#[cfg_attr(test, derive(PartialEq))]
29pub enum PipelineConstantError {
30    #[error("Missing value for pipeline-overridable constant with identifier string: '{0}'")]
31    MissingValue(String),
32    #[error("pipeline-overridable constant '{0}' not found in the shader")]
33    NotFound(String),
34    #[error(
35        "Source f64 value needs to be finite ({}) for number destinations",
36        "NaNs and Inifinites are not allowed"
37    )]
38    SrcNeedsToBeFinite,
39    #[error("Source f64 value doesn't fit in destination")]
40    DstRangeTooSmall,
41    #[error(transparent)]
42    ConstantEvaluatorError(#[from] ConstantEvaluatorError),
43    #[error(transparent)]
44    ValidationError(#[from] Box<WithSpan<ValidationError>>),
45    #[error("workgroup_size override isn't strictly positive")]
46    NegativeWorkgroupSize,
47    #[error("max vertices or max primitives is negative")]
48    NegativeMeshOutputMax,
49}
50
51/// Compact `module` and replace all overrides with constants.
52///
53/// `module` must be valid. Both compaction and constant evaluation may produce
54/// invalid results (e.g. replace an invalid expression with a constant) for
55/// invalid modules.
56///
57/// If `entry_point` is specified, remove all other entry points from the
58/// returned module. Without this, re-validation will fail if any entry point
59/// uses an override whose value wasn't provided.
60///
61/// If no changes are needed, this just returns `Cow::Borrowed` references to
62/// `module` and `module_info`. Otherwise, it clones `module`, retains only the
63/// selected entry point, compacts the module, edits its [`global_expressions`]
64/// arena to contain only fully-evaluated expressions, and returns the
65/// simplified module and its validation results.
66///
67/// The module returned has an empty `overrides` arena, and the
68/// `global_expressions` arena contains only fully-evaluated expressions.
69///
70/// [`global_expressions`]: Module::global_expressions
71pub fn process_overrides<'a>(
72    module: &'a Module,
73    module_info: &'a ModuleInfo,
74    entry_point: Option<(ir::ShaderStage, &str)>,
75    pipeline_constants: &PipelineConstants,
76) -> Result<(Cow<'a, Module>, Cow<'a, ModuleInfo>), PipelineConstantError> {
77    let mut handles = module
78        .overrides
79        .iter()
80        .map(|(handle, _)| handle)
81        .collect::<Vec<_>>();
82    for c in pipeline_constants.keys() {
83        let c_id = c.parse().ok();
84        if let Some((i, _)) = handles.iter().enumerate().find(|&(_, handle)| {
85            let o = &module.overrides[*handle];
86            if o.id.is_some() {
87                o.id == c_id
88            } else {
89                o.name.as_deref() == Some(c.as_str())
90            }
91        }) {
92            handles.swap_remove(i);
93        } else {
94            return Err(PipelineConstantError::NotFound(c.clone()));
95        }
96    }
97
98    if (entry_point.is_none() || module.entry_points.len() <= 1) && module.overrides.is_empty() {
99        // We skip compacting the module here mostly to reduce the risk of
100        // hitting corner cases like https://github.com/gfx-rs/wgpu/issues/7793.
101        // Compaction doesn't cost very much [1], so it would also be reasonable
102        // to do it unconditionally. Even when there is a single entry point or
103        // when no entry point is specified, it is still possible that there
104        // are unreferenced items in the module that would be removed by this
105        // compaction.
106        //
107        // [1]: https://github.com/gfx-rs/wgpu/pull/7703#issuecomment-2902153760
108        return Ok((Cow::Borrowed(module), Cow::Borrowed(module_info)));
109    }
110
111    let mut module = module.clone();
112    if let Some((ep_stage, ep_name)) = entry_point {
113        module
114            .entry_points
115            .retain(|ep| ep.stage == ep_stage && ep.name == ep_name);
116    }
117
118    // Compact the module to remove anything not reachable from an entry point.
119    // This is necessary because we may not have values for overrides that are
120    // not reachable from the/an entry point.
121    compact(&mut module, KeepUnused::No);
122
123    // If there are no overrides in the module, then we can skip the rest.
124    if module.overrides.is_empty() {
125        return revalidate(module);
126    }
127
128    // A map from override handles to the handles of the constants
129    // we've replaced them with.
130    let mut override_map = HandleVec::with_capacity(module.overrides.len());
131
132    // A map from `module`'s original global expression handles to
133    // handles in the new, simplified global expression arena.
134    let mut adjusted_global_expressions = HandleVec::with_capacity(module.global_expressions.len());
135
136    // The set of constants whose initializer handles we've already
137    // updated to refer to the newly built global expression arena.
138    //
139    // All constants in `module` must have their `init` handles
140    // updated to point into the new, simplified global expression
141    // arena. Some of these we can most easily handle as a side effect
142    // during the simplification process, but we must handle the rest
143    // in a final fixup pass, guided by `adjusted_global_expressions`. We
144    // add their handles to this set, so that the final fixup step can
145    // leave them alone.
146    let mut adjusted_constant_initializers = HashSet::with_capacity(module.constants.len());
147
148    let mut global_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();
149    let mut layouter = crate::proc::Layouter::default();
150
151    // An iterator through the original overrides table, consumed in
152    // approximate tandem with the global expressions.
153    let mut overrides = module.overrides.take();
154    let mut override_iter = overrides.iter_mut_span();
155
156    // Do two things in tandem:
157    //
158    // - Rebuild the global expression arena from scratch, fully
159    //   evaluating all expressions, and replacing each `Override`
160    //   expression in `module.global_expressions` with a `Constant`
161    //   expression.
162    //
163    // - Build a new `Constant` in `module.constants` to take the
164    //   place of each `Override`.
165    //
166    // Build a map from old global expression handles to their
167    // fully-evaluated counterparts in `adjusted_global_expressions` as we
168    // go.
169    //
170    // Why in tandem? Overrides refer to expressions, and expressions
171    // refer to overrides, so we can't disentangle the two into
172    // separate phases. However, we can take advantage of the fact
173    // that the overrides and expressions must form a DAG, and work
174    // our way from the leaves to the roots, replacing and evaluating
175    // as we go.
176    //
177    // Although the two loops are nested, this is really two
178    // alternating phases: we adjust and evaluate constant expressions
179    // until we hit an `Override` expression, at which point we switch
180    // to building `Constant`s for `Overrides` until we've handled the
181    // one used by the expression. Then we switch back to processing
182    // expressions. Because we know they form a DAG, we know the
183    // `Override` expressions we encounter can only have initializers
184    // referring to global expressions we've already simplified.
185    for (old_h, expr, span) in module.global_expressions.drain() {
186        let mut expr = match expr {
187            Expression::Override(h) => {
188                let c_h = if let Some(new_h) = override_map.get(h) {
189                    *new_h
190                } else {
191                    let mut new_h = None;
192                    for entry in override_iter.by_ref() {
193                        let stop = entry.0 == h;
194                        new_h = Some(process_override(
195                            entry,
196                            pipeline_constants,
197                            &mut module,
198                            &mut override_map,
199                            &adjusted_global_expressions,
200                            &mut adjusted_constant_initializers,
201                            &mut global_expression_kind_tracker,
202                        )?);
203                        if stop {
204                            break;
205                        }
206                    }
207                    new_h.unwrap()
208                };
209                Expression::Constant(c_h)
210            }
211            Expression::Constant(c_h) => {
212                if adjusted_constant_initializers.insert(c_h) {
213                    let init = &mut module.constants[c_h].init;
214                    *init = adjusted_global_expressions[*init];
215                }
216                expr
217            }
218            expr => expr,
219        };
220        let mut evaluator = ConstantEvaluator::for_wgsl_module(
221            &mut module,
222            &mut global_expression_kind_tracker,
223            &mut layouter,
224            false,
225        );
226        adjust_expr(&adjusted_global_expressions, &mut expr);
227        let h = evaluator.try_eval_and_append(expr, span)?;
228        adjusted_global_expressions.insert(old_h, h);
229    }
230
231    // Finish processing any overrides we didn't visit in the loop above.
232    for entry in override_iter {
233        match *entry.1 {
234            Override { name: Some(_), .. } | Override { id: Some(_), .. } => {
235                process_override(
236                    entry,
237                    pipeline_constants,
238                    &mut module,
239                    &mut override_map,
240                    &adjusted_global_expressions,
241                    &mut adjusted_constant_initializers,
242                    &mut global_expression_kind_tracker,
243                )?;
244            }
245            Override {
246                init: Some(ref mut init),
247                ..
248            } => {
249                *init = adjusted_global_expressions[*init];
250            }
251            _ => {}
252        }
253    }
254
255    // Update the initialization expression handles of all `Constant`s
256    // and `GlobalVariable`s. Skip `Constant`s we'd already updated en
257    // passant.
258    for (_, c) in module
259        .constants
260        .iter_mut()
261        .filter(|&(c_h, _)| !adjusted_constant_initializers.contains(&c_h))
262    {
263        c.init = adjusted_global_expressions[c.init];
264    }
265
266    for (_, v) in module.global_variables.iter_mut() {
267        if let Some(ref mut init) = v.init {
268            *init = adjusted_global_expressions[*init];
269        }
270    }
271
272    let mut functions = module.functions.take();
273    for (_, function) in functions.iter_mut() {
274        process_function(&mut module, &override_map, &mut layouter, function)?;
275    }
276    module.functions = functions;
277
278    let mut entry_points = mem::take(&mut module.entry_points);
279    for ep in entry_points.iter_mut() {
280        process_function(&mut module, &override_map, &mut layouter, &mut ep.function)?;
281        process_workgroup_size_override(&mut module, &adjusted_global_expressions, ep)?;
282        process_mesh_shader_overrides(&mut module, &adjusted_global_expressions, ep)?;
283    }
284    module.entry_points = entry_points;
285    module.overrides = overrides;
286
287    // Now that we've rewritten all the expressions, we need to
288    // recompute their types and other metadata. For the time being,
289    // do a full re-validation.
290    revalidate(module)
291}
292
293fn revalidate(
294    module: Module,
295) -> Result<(Cow<'static, Module>, Cow<'static, ModuleInfo>), PipelineConstantError> {
296    let mut validator = Validator::new(ValidationFlags::all(), Capabilities::all());
297    let module_info = validator.validate_resolved_overrides(&module)?;
298    Ok((Cow::Owned(module), Cow::Owned(module_info)))
299}
300
301fn process_workgroup_size_override(
302    module: &mut Module,
303    adjusted_global_expressions: &HandleVec<Expression, Handle<Expression>>,
304    ep: &mut crate::EntryPoint,
305) -> Result<(), PipelineConstantError> {
306    match ep.workgroup_size_overrides {
307        None => {}
308        Some(overrides) => {
309            overrides.iter().enumerate().try_for_each(
310                |(i, overridden)| -> Result<(), PipelineConstantError> {
311                    match *overridden {
312                        None => Ok(()),
313                        Some(h) => {
314                            ep.workgroup_size[i] = module
315                                .to_ctx()
316                                .get_const_val(adjusted_global_expressions[h])
317                                .map(|n| {
318                                    if n == 0 {
319                                        Err(PipelineConstantError::NegativeWorkgroupSize)
320                                    } else {
321                                        Ok(n)
322                                    }
323                                })
324                                .map_err(|_| PipelineConstantError::NegativeWorkgroupSize)??;
325                            Ok(())
326                        }
327                    }
328                },
329            )?;
330            ep.workgroup_size_overrides = None;
331        }
332    }
333    Ok(())
334}
335
336fn process_mesh_shader_overrides(
337    module: &mut Module,
338    adjusted_global_expressions: &HandleVec<Expression, Handle<Expression>>,
339    ep: &mut crate::EntryPoint,
340) -> Result<(), PipelineConstantError> {
341    if let Some(ref mut mesh_info) = ep.mesh_info {
342        if let Some(r#override) = mesh_info.max_vertices_override {
343            mesh_info.max_vertices = module
344                .to_ctx()
345                .get_const_val(adjusted_global_expressions[r#override])
346                .map_err(|_| PipelineConstantError::NegativeMeshOutputMax)?;
347        }
348        if let Some(r#override) = mesh_info.max_primitives_override {
349            mesh_info.max_primitives = module
350                .to_ctx()
351                .get_const_val(adjusted_global_expressions[r#override])
352                .map_err(|_| PipelineConstantError::NegativeMeshOutputMax)?;
353        }
354    }
355    Ok(())
356}
357
358/// Add a [`Constant`] to `module` for the override `old_h`.
359///
360/// Add the new `Constant` to `override_map` and `adjusted_constant_initializers`.
361fn process_override(
362    (old_h, r#override, span): (Handle<Override>, &mut Override, &Span),
363    pipeline_constants: &PipelineConstants,
364    module: &mut Module,
365    override_map: &mut HandleVec<Override, Handle<Constant>>,
366    adjusted_global_expressions: &HandleVec<Expression, Handle<Expression>>,
367    adjusted_constant_initializers: &mut HashSet<Handle<Constant>>,
368    global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker,
369) -> Result<Handle<Constant>, PipelineConstantError> {
370    // Determine which key to use for `r#override` in `pipeline_constants`.
371    let key = if let Some(id) = r#override.id {
372        Cow::Owned(id.to_string())
373    } else if let Some(ref name) = r#override.name {
374        Cow::Borrowed(name)
375    } else {
376        unreachable!();
377    };
378
379    // Generate a global expression for `r#override`'s value, either
380    // from the provided `pipeline_constants` table or its initializer
381    // in the module.
382    let init = if let Some(value) = pipeline_constants.get::<str>(&key) {
383        let literal = match module.types[r#override.ty].inner {
384            TypeInner::Scalar(scalar) => map_value_to_literal(*value, scalar)?,
385            _ => unreachable!(),
386        };
387        let expr = module
388            .global_expressions
389            .append(Expression::Literal(literal), Span::UNDEFINED);
390        global_expression_kind_tracker.insert(expr, crate::proc::ExpressionKind::Const);
391        expr
392    } else if let Some(init) = r#override.init {
393        adjusted_global_expressions[init]
394    } else {
395        return Err(PipelineConstantError::MissingValue(key.to_string()));
396    };
397
398    // Generate a new `Constant` to represent the override's value.
399    let constant = Constant {
400        name: r#override.name.clone(),
401        ty: r#override.ty,
402        init,
403    };
404    let h = module.constants.append(constant, *span);
405    override_map.insert(old_h, h);
406    adjusted_constant_initializers.insert(h);
407    r#override.init = Some(init);
408    Ok(h)
409}
410
411/// Replace all override expressions in `function` with fully-evaluated constants.
412///
413/// Replace all `Expression::Override`s in `function`'s expression arena with
414/// the corresponding `Expression::Constant`s, as given in `override_map`.
415/// Replace any expressions whose values are now known with their fully
416/// evaluated form.
417///
418/// If `h` is a `Handle<Override>`, then `override_map[h]` is the
419/// `Handle<Constant>` for the override's final value.
420fn process_function(
421    module: &mut Module,
422    override_map: &HandleVec<Override, Handle<Constant>>,
423    layouter: &mut crate::proc::Layouter,
424    function: &mut Function,
425) -> Result<(), ConstantEvaluatorError> {
426    // A map from original local expression handles to
427    // handles in the new, local expression arena.
428    let mut adjusted_local_expressions = HandleVec::with_capacity(function.expressions.len());
429
430    let mut local_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();
431
432    let mut expressions = function.expressions.take();
433
434    // Dummy `emitter` and `block` for the constant evaluator.
435    // We can ignore the concept of emitting expressions here since
436    // expressions have already been covered by a `Statement::Emit`
437    // in the frontend.
438    // The only thing we might have to do is remove some expressions
439    // that have been covered by a `Statement::Emit`. See the docs of
440    // `filter_emits_in_block` for the reasoning.
441    let mut emitter = Emitter::default();
442    let mut block = Block::new();
443
444    let mut evaluator = ConstantEvaluator::for_wgsl_function(
445        module,
446        &mut function.expressions,
447        &mut local_expression_kind_tracker,
448        layouter,
449        &mut emitter,
450        &mut block,
451        false,
452    );
453
454    for (old_h, mut expr, span) in expressions.drain() {
455        if let Expression::Override(h) = expr {
456            expr = Expression::Constant(override_map[h]);
457        }
458        adjust_expr(&adjusted_local_expressions, &mut expr);
459        let h = evaluator.try_eval_and_append(expr, span)?;
460        adjusted_local_expressions.insert(old_h, h);
461    }
462
463    adjust_block(&adjusted_local_expressions, &mut function.body);
464
465    filter_emits_in_block(&mut function.body, &function.expressions);
466
467    // Update local expression initializers.
468    for (_, local) in function.local_variables.iter_mut() {
469        if let &mut Some(ref mut init) = &mut local.init {
470            *init = adjusted_local_expressions[*init];
471        }
472    }
473
474    // We've changed the keys of `function.named_expression`, so we have to
475    // rebuild it from scratch.
476    let named_expressions = mem::take(&mut function.named_expressions);
477    for (expr_h, name) in named_expressions {
478        function
479            .named_expressions
480            .insert(adjusted_local_expressions[expr_h], name);
481    }
482
483    Ok(())
484}
485
486/// Replace every expression handle in `expr` with its counterpart
487/// given by `new_pos`.
488fn adjust_expr(new_pos: &HandleVec<Expression, Handle<Expression>>, expr: &mut Expression) {
489    let adjust = |expr: &mut Handle<Expression>| {
490        *expr = new_pos[*expr];
491    };
492    match *expr {
493        Expression::Compose {
494            ref mut components,
495            ty: _,
496        } => {
497            for c in components.iter_mut() {
498                adjust(c);
499            }
500        }
501        Expression::Access {
502            ref mut base,
503            ref mut index,
504        } => {
505            adjust(base);
506            adjust(index);
507        }
508        Expression::AccessIndex {
509            ref mut base,
510            index: _,
511        } => {
512            adjust(base);
513        }
514        Expression::Splat {
515            ref mut value,
516            size: _,
517        } => {
518            adjust(value);
519        }
520        Expression::Swizzle {
521            ref mut vector,
522            size: _,
523            pattern: _,
524        } => {
525            adjust(vector);
526        }
527        Expression::Load { ref mut pointer } => {
528            adjust(pointer);
529        }
530        Expression::ImageSample {
531            ref mut image,
532            ref mut sampler,
533            ref mut coordinate,
534            ref mut array_index,
535            ref mut offset,
536            ref mut level,
537            ref mut depth_ref,
538            gather: _,
539            clamp_to_edge: _,
540        } => {
541            adjust(image);
542            adjust(sampler);
543            adjust(coordinate);
544            if let Some(e) = array_index.as_mut() {
545                adjust(e);
546            }
547            if let Some(e) = offset.as_mut() {
548                adjust(e);
549            }
550            match *level {
551                crate::SampleLevel::Exact(ref mut expr)
552                | crate::SampleLevel::Bias(ref mut expr) => {
553                    adjust(expr);
554                }
555                crate::SampleLevel::Gradient {
556                    ref mut x,
557                    ref mut y,
558                } => {
559                    adjust(x);
560                    adjust(y);
561                }
562                _ => {}
563            }
564            if let Some(e) = depth_ref.as_mut() {
565                adjust(e);
566            }
567        }
568        Expression::ImageLoad {
569            ref mut image,
570            ref mut coordinate,
571            ref mut array_index,
572            ref mut sample,
573            ref mut level,
574        } => {
575            adjust(image);
576            adjust(coordinate);
577            if let Some(e) = array_index.as_mut() {
578                adjust(e);
579            }
580            if let Some(e) = sample.as_mut() {
581                adjust(e);
582            }
583            if let Some(e) = level.as_mut() {
584                adjust(e);
585            }
586        }
587        Expression::ImageQuery {
588            ref mut image,
589            ref mut query,
590        } => {
591            adjust(image);
592            match *query {
593                crate::ImageQuery::Size { ref mut level } => {
594                    if let Some(e) = level.as_mut() {
595                        adjust(e);
596                    }
597                }
598                crate::ImageQuery::NumLevels
599                | crate::ImageQuery::NumLayers
600                | crate::ImageQuery::NumSamples => {}
601            }
602        }
603        Expression::Unary {
604            ref mut expr,
605            op: _,
606        } => {
607            adjust(expr);
608        }
609        Expression::Binary {
610            ref mut left,
611            ref mut right,
612            op: _,
613        } => {
614            adjust(left);
615            adjust(right);
616        }
617        Expression::Select {
618            ref mut condition,
619            ref mut accept,
620            ref mut reject,
621        } => {
622            adjust(condition);
623            adjust(accept);
624            adjust(reject);
625        }
626        Expression::Derivative {
627            ref mut expr,
628            axis: _,
629            ctrl: _,
630        } => {
631            adjust(expr);
632        }
633        Expression::Relational {
634            ref mut argument,
635            fun: _,
636        } => {
637            adjust(argument);
638        }
639        Expression::Math {
640            ref mut arg,
641            ref mut arg1,
642            ref mut arg2,
643            ref mut arg3,
644            fun: _,
645        } => {
646            adjust(arg);
647            if let Some(e) = arg1.as_mut() {
648                adjust(e);
649            }
650            if let Some(e) = arg2.as_mut() {
651                adjust(e);
652            }
653            if let Some(e) = arg3.as_mut() {
654                adjust(e);
655            }
656        }
657        Expression::As {
658            ref mut expr,
659            kind: _,
660            convert: _,
661        } => {
662            adjust(expr);
663        }
664        Expression::ArrayLength(ref mut expr) => {
665            adjust(expr);
666        }
667        Expression::RayQueryGetIntersection {
668            ref mut query,
669            committed: _,
670        } => {
671            adjust(query);
672        }
673        Expression::Literal(_)
674        | Expression::FunctionArgument(_)
675        | Expression::GlobalVariable(_)
676        | Expression::LocalVariable(_)
677        | Expression::CallResult(_)
678        | Expression::RayQueryProceedResult
679        | Expression::Constant(_)
680        | Expression::Override(_)
681        | Expression::ZeroValue(_)
682        | Expression::AtomicResult {
683            ty: _,
684            comparison: _,
685        }
686        | Expression::WorkGroupUniformLoadResult { ty: _ }
687        | Expression::SubgroupBallotResult
688        | Expression::SubgroupOperationResult { .. } => {}
689        Expression::RayQueryVertexPositions {
690            ref mut query,
691            committed: _,
692        } => {
693            adjust(query);
694        }
695        Expression::CooperativeLoad { ref mut data, .. } => {
696            adjust(&mut data.pointer);
697            adjust(&mut data.stride);
698        }
699        Expression::CooperativeMultiplyAdd {
700            ref mut a,
701            ref mut b,
702            ref mut c,
703        } => {
704            adjust(a);
705            adjust(b);
706            adjust(c);
707        }
708    }
709}
710
711/// Replace every expression handle in `block` with its counterpart
712/// given by `new_pos`.
713fn adjust_block(new_pos: &HandleVec<Expression, Handle<Expression>>, block: &mut Block) {
714    for stmt in block.iter_mut() {
715        adjust_stmt(new_pos, stmt);
716    }
717}
718
719/// Replace every expression handle in `stmt` with its counterpart
720/// given by `new_pos`.
721fn adjust_stmt(new_pos: &HandleVec<Expression, Handle<Expression>>, stmt: &mut Statement) {
722    let adjust = |expr: &mut Handle<Expression>| {
723        *expr = new_pos[*expr];
724    };
725    match *stmt {
726        Statement::Emit(ref mut range) => {
727            if let Some((mut first, mut last)) = range.first_and_last() {
728                adjust(&mut first);
729                adjust(&mut last);
730                *range = Range::new_from_bounds(first, last);
731            }
732        }
733        Statement::Block(ref mut block) => {
734            adjust_block(new_pos, block);
735        }
736        Statement::If {
737            ref mut condition,
738            ref mut accept,
739            ref mut reject,
740        } => {
741            adjust(condition);
742            adjust_block(new_pos, accept);
743            adjust_block(new_pos, reject);
744        }
745        Statement::Switch {
746            ref mut selector,
747            ref mut cases,
748        } => {
749            adjust(selector);
750            for case in cases.iter_mut() {
751                adjust_block(new_pos, &mut case.body);
752            }
753        }
754        Statement::Loop {
755            ref mut body,
756            ref mut continuing,
757            ref mut break_if,
758        } => {
759            adjust_block(new_pos, body);
760            adjust_block(new_pos, continuing);
761            if let Some(e) = break_if.as_mut() {
762                adjust(e);
763            }
764        }
765        Statement::Return { ref mut value } => {
766            if let Some(e) = value.as_mut() {
767                adjust(e);
768            }
769        }
770        Statement::Store {
771            ref mut pointer,
772            ref mut value,
773        } => {
774            adjust(pointer);
775            adjust(value);
776        }
777        Statement::ImageStore {
778            ref mut image,
779            ref mut coordinate,
780            ref mut array_index,
781            ref mut value,
782        } => {
783            adjust(image);
784            adjust(coordinate);
785            if let Some(e) = array_index.as_mut() {
786                adjust(e);
787            }
788            adjust(value);
789        }
790        Statement::Atomic {
791            ref mut pointer,
792            ref mut value,
793            ref mut result,
794            ref mut fun,
795        } => {
796            adjust(pointer);
797            adjust(value);
798            if let Some(ref mut result) = *result {
799                adjust(result);
800            }
801            match *fun {
802                crate::AtomicFunction::Exchange {
803                    compare: Some(ref mut compare),
804                } => {
805                    adjust(compare);
806                }
807                crate::AtomicFunction::Add
808                | crate::AtomicFunction::Subtract
809                | crate::AtomicFunction::And
810                | crate::AtomicFunction::ExclusiveOr
811                | crate::AtomicFunction::InclusiveOr
812                | crate::AtomicFunction::Min
813                | crate::AtomicFunction::Max
814                | crate::AtomicFunction::Exchange { compare: None } => {}
815            }
816        }
817        Statement::ImageAtomic {
818            ref mut image,
819            ref mut coordinate,
820            ref mut array_index,
821            fun: _,
822            ref mut value,
823        } => {
824            adjust(image);
825            adjust(coordinate);
826            if let Some(ref mut array_index) = *array_index {
827                adjust(array_index);
828            }
829            adjust(value);
830        }
831        Statement::WorkGroupUniformLoad {
832            ref mut pointer,
833            ref mut result,
834        } => {
835            adjust(pointer);
836            adjust(result);
837        }
838        Statement::SubgroupBallot {
839            ref mut result,
840            ref mut predicate,
841        } => {
842            if let Some(ref mut predicate) = *predicate {
843                adjust(predicate);
844            }
845            adjust(result);
846        }
847        Statement::SubgroupCollectiveOperation {
848            ref mut argument,
849            ref mut result,
850            ..
851        } => {
852            adjust(argument);
853            adjust(result);
854        }
855        Statement::SubgroupGather {
856            ref mut mode,
857            ref mut argument,
858            ref mut result,
859        } => {
860            match *mode {
861                crate::GatherMode::BroadcastFirst => {}
862                crate::GatherMode::Broadcast(ref mut index)
863                | crate::GatherMode::Shuffle(ref mut index)
864                | crate::GatherMode::ShuffleDown(ref mut index)
865                | crate::GatherMode::ShuffleUp(ref mut index)
866                | crate::GatherMode::ShuffleXor(ref mut index)
867                | crate::GatherMode::QuadBroadcast(ref mut index) => {
868                    adjust(index);
869                }
870                crate::GatherMode::QuadSwap(_) => {}
871            }
872            adjust(argument);
873            adjust(result)
874        }
875        Statement::Call {
876            ref mut arguments,
877            ref mut result,
878            function: _,
879        } => {
880            for argument in arguments.iter_mut() {
881                adjust(argument);
882            }
883            if let Some(e) = result.as_mut() {
884                adjust(e);
885            }
886        }
887        Statement::RayQuery {
888            ref mut query,
889            ref mut fun,
890        } => {
891            adjust(query);
892            match *fun {
893                crate::RayQueryFunction::Initialize {
894                    ref mut acceleration_structure,
895                    ref mut descriptor,
896                } => {
897                    adjust(acceleration_structure);
898                    adjust(descriptor);
899                }
900                crate::RayQueryFunction::Proceed { ref mut result } => {
901                    adjust(result);
902                }
903                crate::RayQueryFunction::GenerateIntersection { ref mut hit_t } => {
904                    adjust(hit_t);
905                }
906                crate::RayQueryFunction::ConfirmIntersection => {}
907                crate::RayQueryFunction::Terminate => {}
908            }
909        }
910        Statement::CooperativeStore {
911            ref mut target,
912            ref mut data,
913        } => {
914            adjust(target);
915            adjust(&mut data.pointer);
916            adjust(&mut data.stride);
917        }
918        Statement::RayPipelineFunction(ref mut func) => match *func {
919            crate::RayPipelineFunction::TraceRay {
920                ref mut acceleration_structure,
921                ref mut descriptor,
922                ref mut payload,
923            } => {
924                adjust(acceleration_structure);
925                adjust(descriptor);
926                adjust(payload);
927            }
928        },
929        Statement::Break
930        | Statement::Continue
931        | Statement::Kill
932        | Statement::ControlBarrier(_)
933        | Statement::MemoryBarrier(_) => {}
934    }
935}
936
937/// Adjust [`Emit`] statements in `block` to skip [`needs_pre_emit`] expressions we have introduced.
938///
939/// According to validation, [`Emit`] statements must not cover any expressions
940/// for which [`Expression::needs_pre_emit`] returns true. All expressions built
941/// by successful constant evaluation fall into that category, meaning that
942/// `process_function` will usually rewrite [`Override`] expressions and those
943/// that use their values into pre-emitted expressions, leaving any [`Emit`]
944/// statements that cover them invalid.
945///
946/// This function rewrites all [`Emit`] statements into zero or more new
947/// [`Emit`] statements covering only those expressions in the original range
948/// that are not pre-emitted.
949///
950/// [`Emit`]: Statement::Emit
951/// [`needs_pre_emit`]: Expression::needs_pre_emit
952/// [`Override`]: Expression::Override
953fn filter_emits_in_block(block: &mut Block, expressions: &Arena<Expression>) {
954    let original = mem::replace(block, Block::with_capacity(block.len()));
955    for (stmt, span) in original.span_into_iter() {
956        match stmt {
957            Statement::Emit(range) => {
958                let mut current = None;
959                for expr_h in range {
960                    if expressions[expr_h].needs_pre_emit() {
961                        if let Some((first, last)) = current {
962                            block.push(Statement::Emit(Range::new_from_bounds(first, last)), span);
963                        }
964
965                        current = None;
966                    } else if let Some((_, ref mut last)) = current {
967                        *last = expr_h;
968                    } else {
969                        current = Some((expr_h, expr_h));
970                    }
971                }
972                if let Some((first, last)) = current {
973                    block.push(Statement::Emit(Range::new_from_bounds(first, last)), span);
974                }
975            }
976            Statement::Block(mut child) => {
977                filter_emits_in_block(&mut child, expressions);
978                block.push(Statement::Block(child), span);
979            }
980            Statement::If {
981                condition,
982                mut accept,
983                mut reject,
984            } => {
985                filter_emits_in_block(&mut accept, expressions);
986                filter_emits_in_block(&mut reject, expressions);
987                block.push(
988                    Statement::If {
989                        condition,
990                        accept,
991                        reject,
992                    },
993                    span,
994                );
995            }
996            Statement::Switch {
997                selector,
998                mut cases,
999            } => {
1000                for case in &mut cases {
1001                    filter_emits_in_block(&mut case.body, expressions);
1002                }
1003                block.push(Statement::Switch { selector, cases }, span);
1004            }
1005            Statement::Loop {
1006                mut body,
1007                mut continuing,
1008                break_if,
1009            } => {
1010                filter_emits_in_block(&mut body, expressions);
1011                filter_emits_in_block(&mut continuing, expressions);
1012                block.push(
1013                    Statement::Loop {
1014                        body,
1015                        continuing,
1016                        break_if,
1017                    },
1018                    span,
1019                );
1020            }
1021            stmt => block.push(stmt.clone(), span),
1022        }
1023    }
1024}
1025
1026fn map_value_to_literal(value: f64, scalar: Scalar) -> Result<Literal, PipelineConstantError> {
1027    // note that in rust 0.0 == -0.0
1028    match scalar {
1029        Scalar::BOOL => {
1030            // https://webidl.spec.whatwg.org/#js-boolean
1031            let value = value != 0.0 && !value.is_nan();
1032            Ok(Literal::Bool(value))
1033        }
1034        Scalar::I16 => {
1035            if !value.is_finite() {
1036                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1037            }
1038
1039            let value = value.trunc();
1040            if value < f64::from(i16::MIN) || value > f64::from(i16::MAX) {
1041                return Err(PipelineConstantError::DstRangeTooSmall);
1042            }
1043
1044            let value = value as i16;
1045            Ok(Literal::I16(value))
1046        }
1047        Scalar::U16 => {
1048            if !value.is_finite() {
1049                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1050            }
1051
1052            let value = value.trunc();
1053            if value < f64::from(u16::MIN) || value > f64::from(u16::MAX) {
1054                return Err(PipelineConstantError::DstRangeTooSmall);
1055            }
1056
1057            let value = value as u16;
1058            Ok(Literal::U16(value))
1059        }
1060        Scalar::I32 => {
1061            // https://webidl.spec.whatwg.org/#js-long
1062            if !value.is_finite() {
1063                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1064            }
1065
1066            let value = value.trunc();
1067            if value < f64::from(i32::MIN) || value > f64::from(i32::MAX) {
1068                return Err(PipelineConstantError::DstRangeTooSmall);
1069            }
1070
1071            let value = value as i32;
1072            Ok(Literal::I32(value))
1073        }
1074        Scalar::U32 => {
1075            // https://webidl.spec.whatwg.org/#js-unsigned-long
1076            if !value.is_finite() {
1077                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1078            }
1079
1080            let value = value.trunc();
1081            if value < f64::from(u32::MIN) || value > f64::from(u32::MAX) {
1082                return Err(PipelineConstantError::DstRangeTooSmall);
1083            }
1084
1085            let value = value as u32;
1086            Ok(Literal::U32(value))
1087        }
1088        Scalar::F16 => {
1089            // https://webidl.spec.whatwg.org/#js-float
1090            if !value.is_finite() {
1091                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1092            }
1093
1094            let value = half::f16::from_f64(value);
1095            if !value.is_finite() {
1096                return Err(PipelineConstantError::DstRangeTooSmall);
1097            }
1098
1099            Ok(Literal::F16(value))
1100        }
1101        Scalar::F32 => {
1102            // https://webidl.spec.whatwg.org/#js-float
1103            if !value.is_finite() {
1104                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1105            }
1106
1107            let value = value as f32;
1108            if !value.is_finite() {
1109                return Err(PipelineConstantError::DstRangeTooSmall);
1110            }
1111
1112            Ok(Literal::F32(value))
1113        }
1114        Scalar::F64 => {
1115            // https://webidl.spec.whatwg.org/#js-double
1116            if !value.is_finite() {
1117                return Err(PipelineConstantError::SrcNeedsToBeFinite);
1118            }
1119
1120            Ok(Literal::F64(value))
1121        }
1122        Scalar::ABSTRACT_FLOAT | Scalar::ABSTRACT_INT => {
1123            unreachable!("abstract values should not be validated out of override processing")
1124        }
1125        _ => unreachable!("unrecognized scalar type for override"),
1126    }
1127}
1128
1129#[test]
1130fn test_map_value_to_literal() {
1131    let bool_test_cases = [
1132        (0.0, false),
1133        (-0.0, false),
1134        (f64::NAN, false),
1135        (1.0, true),
1136        (f64::INFINITY, true),
1137        (f64::NEG_INFINITY, true),
1138    ];
1139    for (value, out) in bool_test_cases {
1140        let res = Ok(Literal::Bool(out));
1141        assert_eq!(map_value_to_literal(value, Scalar::BOOL), res);
1142    }
1143
1144    for scalar in [Scalar::I32, Scalar::U32, Scalar::F32, Scalar::F64] {
1145        for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
1146            let res = Err(PipelineConstantError::SrcNeedsToBeFinite);
1147            assert_eq!(map_value_to_literal(value, scalar), res);
1148        }
1149    }
1150
1151    // i32
1152    assert_eq!(
1153        map_value_to_literal(f64::from(i32::MIN), Scalar::I32),
1154        Ok(Literal::I32(i32::MIN))
1155    );
1156    assert_eq!(
1157        map_value_to_literal(f64::from(i32::MAX), Scalar::I32),
1158        Ok(Literal::I32(i32::MAX))
1159    );
1160    assert_eq!(
1161        map_value_to_literal(f64::from(i32::MIN) - 1.0, Scalar::I32),
1162        Err(PipelineConstantError::DstRangeTooSmall)
1163    );
1164    assert_eq!(
1165        map_value_to_literal(f64::from(i32::MAX) + 1.0, Scalar::I32),
1166        Err(PipelineConstantError::DstRangeTooSmall)
1167    );
1168
1169    // u32
1170    assert_eq!(
1171        map_value_to_literal(f64::from(u32::MIN), Scalar::U32),
1172        Ok(Literal::U32(u32::MIN))
1173    );
1174    assert_eq!(
1175        map_value_to_literal(f64::from(u32::MAX), Scalar::U32),
1176        Ok(Literal::U32(u32::MAX))
1177    );
1178    assert_eq!(
1179        map_value_to_literal(f64::from(u32::MIN) - 1.0, Scalar::U32),
1180        Err(PipelineConstantError::DstRangeTooSmall)
1181    );
1182    assert_eq!(
1183        map_value_to_literal(f64::from(u32::MAX) + 1.0, Scalar::U32),
1184        Err(PipelineConstantError::DstRangeTooSmall)
1185    );
1186
1187    // f32
1188    assert_eq!(
1189        map_value_to_literal(f64::from(f32::MIN), Scalar::F32),
1190        Ok(Literal::F32(f32::MIN))
1191    );
1192    assert_eq!(
1193        map_value_to_literal(f64::from(f32::MAX), Scalar::F32),
1194        Ok(Literal::F32(f32::MAX))
1195    );
1196    assert_eq!(
1197        map_value_to_literal(-f64::from_bits(0x47efffffefffffff), Scalar::F32),
1198        Ok(Literal::F32(f32::MIN))
1199    );
1200    assert_eq!(
1201        map_value_to_literal(f64::from_bits(0x47efffffefffffff), Scalar::F32),
1202        Ok(Literal::F32(f32::MAX))
1203    );
1204    assert_eq!(
1205        map_value_to_literal(-f64::from_bits(0x47effffff0000000), Scalar::F32),
1206        Err(PipelineConstantError::DstRangeTooSmall)
1207    );
1208    assert_eq!(
1209        map_value_to_literal(f64::from_bits(0x47effffff0000000), Scalar::F32),
1210        Err(PipelineConstantError::DstRangeTooSmall)
1211    );
1212
1213    // f64
1214    assert_eq!(
1215        map_value_to_literal(f64::MIN, Scalar::F64),
1216        Ok(Literal::F64(f64::MIN))
1217    );
1218    assert_eq!(
1219        map_value_to_literal(f64::MAX, Scalar::F64),
1220        Ok(Literal::F64(f64::MAX))
1221    );
1222}