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