naga/compact/
mod.rs

1mod expressions;
2mod functions;
3mod handle_set_map;
4mod statements;
5mod types;
6
7use alloc::vec::Vec;
8
9use crate::{
10    arena::{self, HandleSet},
11    compact::functions::FunctionTracer,
12    ir,
13};
14use handle_set_map::HandleMap;
15
16#[cfg(test)]
17use alloc::{format, string::ToString};
18
19/// Configuration option for [`compact`]. See [`compact`] for details.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum KeepUnused {
22    No,
23    Yes,
24}
25
26impl From<KeepUnused> for bool {
27    fn from(keep_unused: KeepUnused) -> Self {
28        match keep_unused {
29            KeepUnused::No => false,
30            KeepUnused::Yes => true,
31        }
32    }
33}
34
35/// Remove most unused objects from `module`, which must be valid.
36///
37/// Always removes the following unused objects:
38/// - anonymous types, overrides, and constants
39/// - abstract-typed constants
40/// - expressions
41///
42/// If `keep_unused` is `Yes`, the following are never considered unused,
43/// otherwise, they will also be removed if unused:
44/// - functions
45/// - global variables
46/// - named types and overrides
47///
48/// The following are never removed:
49/// - named constants with a concrete type
50/// - special types
51/// - entry points
52/// - within an entry point or a used function:
53///     - arguments
54///     - local variables
55///     - named expressions
56///
57/// After removing items according to the rules above, all handles in the
58/// remaining objects are adjusted as necessary. When `KeepUnused` is `Yes`, the
59/// resulting module should have all the named objects (except abstract-typed
60/// constants) present in the original, and those objects should be functionally
61/// identical. When `KeepUnused` is `No`, the resulting module should have the
62/// entry points present in the original, and those entry points should be
63/// functionally identical.
64///
65/// # Panics
66///
67/// If `module` would not pass validation, this may panic.
68pub fn compact(module: &mut crate::Module, keep_unused: KeepUnused) {
69    // The trickiest part of compaction is determining what is used and what is
70    // not. Once we have computed that correctly, it's easy enough to call
71    // `retain_mut` on each arena, drop unused elements, and fix up the handles
72    // in what's left.
73    //
74    // For every compactable arena in a `Module`, whether global to the `Module`
75    // or local to a function or entry point, the `ModuleTracer` type holds a
76    // bitmap indicating which elements of that arena are used. Our task is to
77    // populate those bitmaps correctly.
78    //
79    // First, we mark everything that is considered used by definition, as
80    // described in this function's documentation.
81    //
82    // Since functions and entry points are considered used by definition, we
83    // traverse their statement trees, and mark the referents of all handles
84    // appearing in those statements as used.
85    //
86    // Once we've marked which elements of an arena are referred to directly by
87    // handles elsewhere (for example, which of a function's expressions are
88    // referred to by handles in its body statements), we can mark all the other
89    // arena elements that are used indirectly in a single pass, traversing the
90    // arena from back to front. Since Naga allows arena elements to refer only
91    // to prior elements, we know that by the time we reach an element, all
92    // other elements that could possibly refer to it have already been visited.
93    // Thus, if the present element has not been marked as used, then it is
94    // definitely unused, and compaction can remove it. Otherwise, the element
95    // is used and must be retained, so we must mark everything it refers to.
96    //
97    // The final step is to mark the global expressions and types, which must be
98    // traversed simultaneously; see `ModuleTracer::type_expression_tandem`'s
99    // documentation for details.
100    //
101    // # A definition and a rule of thumb
102    //
103    // In this module, to "trace" something is to mark everything else it refers
104    // to as used, on the assumption that the thing itself is used. For example,
105    // to trace an `Expression` is to mark its subexpressions as used, as well
106    // as any types, constants, overrides, etc. that it refers to. This is what
107    // `ExpressionTracer::trace_expression` does.
108    //
109    // Given that we we want to visit each thing only once (to keep compaction
110    // linear in the size of the module), this definition of "trace" implies
111    // that things that are not "used by definition" must be marked as used
112    // *before* we trace them.
113    //
114    // Thus, whenever you are marking something as used, it's a good idea to ask
115    // yourself how you know that thing will be traced in the future. If you're
116    // not sure, then you could be marking it too late to be noticed. The thing
117    // itself will be retained by compaction, but since it will not be traced,
118    // anything it refers to could be compacted away.
119    let mut module_tracer = ModuleTracer::new(module);
120
121    // Observe what each entry point actually uses.
122    log::trace!("tracing entry points");
123    let entry_point_maps = module
124        .entry_points
125        .iter()
126        .map(|e| {
127            log::trace!("tracing entry point {:?}", e.function.name);
128
129            if let Some(sizes) = e.workgroup_size_overrides {
130                for size in sizes.iter().filter_map(|x| *x) {
131                    module_tracer.global_expressions_used.insert(size);
132                }
133            }
134
135            if let Some(task_payload) = e.task_payload {
136                module_tracer.global_variables_used.insert(task_payload);
137            }
138            if let Some(ref mesh_info) = e.mesh_info {
139                module_tracer
140                    .global_variables_used
141                    .insert(mesh_info.output_variable);
142                module_tracer
143                    .types_used
144                    .insert(mesh_info.vertex_output_type);
145                module_tracer
146                    .types_used
147                    .insert(mesh_info.primitive_output_type);
148                if let Some(max_vertices_override) = mesh_info.max_vertices_override {
149                    module_tracer
150                        .global_expressions_used
151                        .insert(max_vertices_override);
152                }
153                if let Some(max_primitives_override) = mesh_info.max_primitives_override {
154                    module_tracer
155                        .global_expressions_used
156                        .insert(max_primitives_override);
157                }
158            }
159            if e.stage == crate::ShaderStage::Task || e.stage == crate::ShaderStage::Mesh {
160                // u32 should always be there if the module is valid, as it is e.g. the type of some expressions
161                let u32_type = module
162                    .types
163                    .iter()
164                    .find_map(|tuple| {
165                        if tuple.1.inner == crate::TypeInner::Scalar(crate::Scalar::U32) {
166                            Some(tuple.0)
167                        } else {
168                            None
169                        }
170                    })
171                    .unwrap();
172                module_tracer.types_used.insert(u32_type);
173            }
174
175            let mut used = module_tracer.as_function(&e.function);
176            used.trace();
177            FunctionMap::from(used)
178        })
179        .collect::<Vec<_>>();
180
181    // Observe which types, constant expressions, constants, and expressions
182    // each function uses, and produce maps for each function from
183    // pre-compaction to post-compaction expression handles.
184    //
185    // The function tracing logic here works in conjunction with
186    // `FunctionTracer::trace_call`, which, when tracing a `Statement::Call`
187    // to a function not already identified as used, adds the called function
188    // to both `functions_used` and `functions_pending`.
189    //
190    // Called functions are required to appear before their callers in the
191    // functions arena (recursion is disallowed). We have already traced the
192    // entry point(s) and added any functions called directly by the entry
193    // point(s) to `functions_pending`. We proceed by repeatedly tracing the
194    // last function in `functions_pending`. By an inductive argument, any
195    // functions after the last function in `functions_pending` must be unused.
196    //
197    // When `KeepUnused` is active, we simply mark all functions as pending,
198    // and then trace all of them.
199    log::trace!("tracing functions");
200    let mut function_maps = HandleMap::with_capacity(module.functions.len());
201    if keep_unused.into() {
202        module_tracer.functions_used.add_all();
203        module_tracer.functions_pending.add_all();
204    }
205    while let Some(handle) = module_tracer.functions_pending.pop() {
206        let function = &module.functions[handle];
207        log::trace!("tracing function {function:?}");
208        let mut function_tracer = module_tracer.as_function(function);
209        function_tracer.trace();
210        function_maps.insert(handle, FunctionMap::from(function_tracer));
211    }
212
213    // We treat all special types as used by definition.
214    log::trace!("tracing special types");
215    module_tracer.trace_special_types(&module.special_types);
216
217    log::trace!("tracing global variables");
218    if keep_unused.into() {
219        module_tracer.global_variables_used.add_all();
220    }
221    for global in module_tracer.global_variables_used.iter() {
222        log::trace!("tracing global {:?}", module.global_variables[global].name);
223        module_tracer
224            .types_used
225            .insert(module.global_variables[global].ty);
226        if let Some(init) = module.global_variables[global].init {
227            module_tracer.global_expressions_used.insert(init);
228        }
229    }
230
231    // We treat all named constants as used by definition, unless they have an
232    // abstract type as we do not want those reaching the validator.
233    log::trace!("tracing named constants");
234    for (handle, constant) in module.constants.iter() {
235        if constant.name.is_none() || module.types[constant.ty].inner.is_abstract(&module.types) {
236            continue;
237        }
238
239        log::trace!("tracing constant {:?}", constant.name.as_ref().unwrap());
240        module_tracer.constants_used.insert(handle);
241        module_tracer.types_used.insert(constant.ty);
242        module_tracer.global_expressions_used.insert(constant.init);
243    }
244
245    if keep_unused.into() {
246        // Treat all named overrides as used.
247        for (handle, r#override) in module.overrides.iter() {
248            if r#override.name.is_some() && module_tracer.overrides_used.insert(handle) {
249                module_tracer.types_used.insert(r#override.ty);
250                if let Some(init) = r#override.init {
251                    module_tracer.global_expressions_used.insert(init);
252                }
253            }
254        }
255
256        // Treat all named types as used.
257        for (handle, ty) in module.types.iter() {
258            if ty.name.is_some() {
259                module_tracer.types_used.insert(handle);
260            }
261        }
262    }
263
264    module_tracer.type_expression_tandem();
265
266    // Now that we know what is used and what is never touched,
267    // produce maps from the `Handle`s that appear in `module` now to
268    // the corresponding `Handle`s that will refer to the same items
269    // in the compacted module.
270    let module_map = ModuleMap::from(module_tracer);
271
272    // Drop unused types from the type arena.
273    //
274    // `FastIndexSet`s don't have an underlying Vec<T> that we can
275    // steal, compact in place, and then rebuild the `FastIndexSet`
276    // from. So we have to rebuild the type arena from scratch.
277    log::trace!("compacting types");
278    let mut new_types = arena::UniqueArena::new();
279    for (old_handle, mut ty, span) in module.types.drain_all() {
280        if let Some(expected_new_handle) = module_map.types.try_adjust(old_handle) {
281            module_map.adjust_type(&mut ty);
282            let actual_new_handle = new_types.insert(ty, span);
283            assert_eq!(actual_new_handle, expected_new_handle);
284        }
285    }
286    module.types = new_types;
287    log::trace!("adjusting special types");
288    module_map.adjust_special_types(&mut module.special_types);
289
290    // Drop unused constant expressions, reusing existing storage.
291    log::trace!("adjusting constant expressions");
292    module.global_expressions.retain_mut(|handle, expr| {
293        if module_map.global_expressions.used(handle) {
294            module_map.adjust_expression(expr, &module_map.global_expressions);
295            true
296        } else {
297            false
298        }
299    });
300
301    // Drop unused constants in place, reusing existing storage.
302    log::trace!("adjusting constants");
303    module.constants.retain_mut(|handle, constant| {
304        if module_map.constants.used(handle) {
305            module_map.types.adjust(&mut constant.ty);
306            module_map.global_expressions.adjust(&mut constant.init);
307            true
308        } else {
309            false
310        }
311    });
312
313    // Drop unused overrides in place, reusing existing storage.
314    log::trace!("adjusting overrides");
315    module.overrides.retain_mut(|handle, r#override| {
316        if module_map.overrides.used(handle) {
317            module_map.types.adjust(&mut r#override.ty);
318            if let Some(ref mut init) = r#override.init {
319                module_map.global_expressions.adjust(init);
320            }
321            true
322        } else {
323            false
324        }
325    });
326
327    // Adjust workgroup_size_overrides
328    log::trace!("adjusting workgroup_size_overrides");
329    for e in module.entry_points.iter_mut() {
330        if let Some(sizes) = e.workgroup_size_overrides.as_mut() {
331            for size in sizes.iter_mut() {
332                if let Some(expr) = size.as_mut() {
333                    module_map.global_expressions.adjust(expr);
334                }
335            }
336        }
337    }
338
339    // Drop unused global variables, reusing existing storage.
340    // Adjust used global variables' types and initializers.
341    log::trace!("adjusting global variables");
342    module.global_variables.retain_mut(|handle, global| {
343        if module_map.globals.used(handle) {
344            log::trace!("retaining global variable {:?}", global.name);
345            module_map.types.adjust(&mut global.ty);
346            if let Some(ref mut init) = global.init {
347                module_map.global_expressions.adjust(init);
348            }
349            true
350        } else {
351            log::trace!("dropping global variable {:?}", global.name);
352            false
353        }
354    });
355
356    // Adjust doc comments
357    if let Some(ref mut doc_comments) = module.doc_comments {
358        module_map.adjust_doc_comments(doc_comments.as_mut());
359    }
360
361    // Temporary storage to help us reuse allocations of existing
362    // named expression tables.
363    let mut reused_named_expressions = crate::NamedExpressions::default();
364
365    // Drop unused functions. Compact and adjust used functions.
366    module.functions.retain_mut(|handle, function| {
367        if let Some(map) = function_maps.get(handle) {
368            log::trace!("retaining and compacting function {:?}", function.name);
369            map.compact(function, &module_map, &mut reused_named_expressions);
370            true
371        } else {
372            log::trace!("dropping function {:?}", function.name);
373            false
374        }
375    });
376
377    // Compact each entry point.
378    for (entry, map) in module.entry_points.iter_mut().zip(entry_point_maps.iter()) {
379        log::trace!("compacting entry point {:?}", entry.function.name);
380        map.compact(
381            &mut entry.function,
382            &module_map,
383            &mut reused_named_expressions,
384        );
385        if let Some(ref mut task_payload) = entry.task_payload {
386            module_map.globals.adjust(task_payload);
387        }
388        if let Some(ref mut mesh_info) = entry.mesh_info {
389            module_map.globals.adjust(&mut mesh_info.output_variable);
390            module_map.types.adjust(&mut mesh_info.vertex_output_type);
391            module_map
392                .types
393                .adjust(&mut mesh_info.primitive_output_type);
394            if let Some(ref mut max_vertices_override) = mesh_info.max_vertices_override {
395                module_map.global_expressions.adjust(max_vertices_override);
396            }
397            if let Some(ref mut max_primitives_override) = mesh_info.max_primitives_override {
398                module_map
399                    .global_expressions
400                    .adjust(max_primitives_override);
401            }
402        }
403    }
404}
405
406struct ModuleTracer<'module> {
407    module: &'module crate::Module,
408
409    /// The subset of functions in `functions_used` that have not yet been
410    /// traced.
411    functions_pending: HandleSet<crate::Function>,
412
413    functions_used: HandleSet<crate::Function>,
414    types_used: HandleSet<crate::Type>,
415    global_variables_used: HandleSet<crate::GlobalVariable>,
416    constants_used: HandleSet<crate::Constant>,
417    overrides_used: HandleSet<crate::Override>,
418    global_expressions_used: HandleSet<crate::Expression>,
419}
420
421impl<'module> ModuleTracer<'module> {
422    fn new(module: &'module crate::Module) -> Self {
423        Self {
424            module,
425            functions_pending: HandleSet::for_arena(&module.functions),
426            functions_used: HandleSet::for_arena(&module.functions),
427            types_used: HandleSet::for_arena(&module.types),
428            global_variables_used: HandleSet::for_arena(&module.global_variables),
429            constants_used: HandleSet::for_arena(&module.constants),
430            overrides_used: HandleSet::for_arena(&module.overrides),
431            global_expressions_used: HandleSet::for_arena(&module.global_expressions),
432        }
433    }
434
435    fn trace_special_types(&mut self, special_types: &crate::SpecialTypes) {
436        let crate::SpecialTypes {
437            ref ray_desc,
438            ref ray_intersection,
439            ref ray_vertex_return,
440            ref predeclared_types,
441            ref external_texture_params,
442            ref external_texture_transfer_function,
443        } = *special_types;
444
445        if let Some(ray_desc) = *ray_desc {
446            self.types_used.insert(ray_desc);
447        }
448        if let Some(ray_intersection) = *ray_intersection {
449            self.types_used.insert(ray_intersection);
450        }
451        if let Some(ray_vertex_return) = *ray_vertex_return {
452            self.types_used.insert(ray_vertex_return);
453        }
454        // The `external_texture_params` type is generated purely as a
455        // convenience to the backends. While it will never actually be used in
456        // the IR, it must be marked as used so that it survives compaction.
457        if let Some(external_texture_params) = *external_texture_params {
458            self.types_used.insert(external_texture_params);
459        }
460        if let Some(external_texture_transfer_function) = *external_texture_transfer_function {
461            self.types_used.insert(external_texture_transfer_function);
462        }
463        for (_, &handle) in predeclared_types {
464            self.types_used.insert(handle);
465        }
466    }
467
468    /// Traverse types and global expressions in tandem to determine which are used.
469    ///
470    /// Assuming that all types and global expressions used by other parts of
471    /// the module have been added to [`types_used`] and
472    /// [`global_expressions_used`], expand those sets to include all types and
473    /// global expressions reachable from those.
474    ///
475    /// [`types_used`]: ModuleTracer::types_used
476    /// [`global_expressions_used`]: ModuleTracer::global_expressions_used
477    fn type_expression_tandem(&mut self) {
478        // For each type T, compute the latest global expression E that T and
479        // its predecessors refer to. Given the ordering rules on types and
480        // global expressions in valid modules, we can do this with a single
481        // forward scan of the type arena. The rules further imply that T can
482        // only be referred to by expressions after E.
483        let mut max_dep = Vec::with_capacity(self.module.types.len());
484        let mut previous = None;
485        for (_handle, ty) in self.module.types.iter() {
486            previous = core::cmp::max(
487                previous,
488                match ty.inner {
489                    crate::TypeInner::Array { size, .. }
490                    | crate::TypeInner::BindingArray { size, .. } => match size {
491                        crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => None,
492                        crate::ArraySize::Pending(handle) => self.module.overrides[handle].init,
493                    },
494                    _ => None,
495                },
496            );
497            max_dep.push(previous);
498        }
499
500        // Visit types and global expressions from youngest to oldest.
501        //
502        // The outer loop visits types. Before visiting each type, the inner
503        // loop ensures that all global expressions that could possibly refer to
504        // it have been visited. And since the inner loop stop at the latest
505        // expression that the type could possibly refer to, we know that we
506        // have previously visited any types that might refer to each expression
507        // we visit.
508        //
509        // This lets us assume that any type or expression that is *not* marked
510        // as used by the time we visit it is genuinely unused, and can be
511        // ignored.
512        let mut exprs = self.module.global_expressions.iter().rev().peekable();
513
514        for ((ty_handle, ty), dep) in self.module.types.iter().zip(max_dep).rev() {
515            while let Some((expr_handle, expr)) = exprs.next_if(|&(h, _)| Some(h) > dep) {
516                if self.global_expressions_used.contains(expr_handle) {
517                    self.as_const_expression().trace_expression(expr);
518                }
519            }
520            if self.types_used.contains(ty_handle) {
521                self.as_type().trace_type(ty);
522            }
523        }
524        // Visit any remaining expressions.
525        for (expr_handle, expr) in exprs {
526            if self.global_expressions_used.contains(expr_handle) {
527                self.as_const_expression().trace_expression(expr);
528            }
529        }
530    }
531
532    fn as_type(&mut self) -> types::TypeTracer<'_> {
533        types::TypeTracer {
534            overrides: &self.module.overrides,
535            types_used: &mut self.types_used,
536            expressions_used: &mut self.global_expressions_used,
537            overrides_used: &mut self.overrides_used,
538        }
539    }
540
541    fn as_const_expression(&mut self) -> expressions::ExpressionTracer<'_> {
542        expressions::ExpressionTracer {
543            constants: &self.module.constants,
544            overrides: &self.module.overrides,
545            expressions: &self.module.global_expressions,
546            types_used: &mut self.types_used,
547            global_variables_used: &mut self.global_variables_used,
548            constants_used: &mut self.constants_used,
549            expressions_used: &mut self.global_expressions_used,
550            overrides_used: &mut self.overrides_used,
551            global_expressions_used: None,
552        }
553    }
554
555    pub fn as_function<'tracer>(
556        &'tracer mut self,
557        function: &'tracer crate::Function,
558    ) -> FunctionTracer<'tracer> {
559        FunctionTracer {
560            function,
561            constants: &self.module.constants,
562            overrides: &self.module.overrides,
563            functions_pending: &mut self.functions_pending,
564            functions_used: &mut self.functions_used,
565            types_used: &mut self.types_used,
566            global_variables_used: &mut self.global_variables_used,
567            constants_used: &mut self.constants_used,
568            overrides_used: &mut self.overrides_used,
569            global_expressions_used: &mut self.global_expressions_used,
570            expressions_used: HandleSet::for_arena(&function.expressions),
571        }
572    }
573}
574
575struct ModuleMap {
576    functions: HandleMap<crate::Function>,
577    types: HandleMap<crate::Type>,
578    globals: HandleMap<crate::GlobalVariable>,
579    constants: HandleMap<crate::Constant>,
580    overrides: HandleMap<crate::Override>,
581    global_expressions: HandleMap<crate::Expression>,
582}
583
584impl From<ModuleTracer<'_>> for ModuleMap {
585    fn from(used: ModuleTracer) -> Self {
586        ModuleMap {
587            functions: HandleMap::from_set(used.functions_used),
588            types: HandleMap::from_set(used.types_used),
589            globals: HandleMap::from_set(used.global_variables_used),
590            constants: HandleMap::from_set(used.constants_used),
591            overrides: HandleMap::from_set(used.overrides_used),
592            global_expressions: HandleMap::from_set(used.global_expressions_used),
593        }
594    }
595}
596
597impl ModuleMap {
598    fn adjust_special_types(&self, special: &mut crate::SpecialTypes) {
599        let crate::SpecialTypes {
600            ref mut ray_desc,
601            ref mut ray_intersection,
602            ref mut ray_vertex_return,
603            ref mut predeclared_types,
604            ref mut external_texture_params,
605            ref mut external_texture_transfer_function,
606        } = *special;
607
608        if let Some(ref mut ray_desc) = *ray_desc {
609            self.types.adjust(ray_desc);
610        }
611        if let Some(ref mut ray_intersection) = *ray_intersection {
612            self.types.adjust(ray_intersection);
613        }
614
615        if let Some(ref mut ray_vertex_return) = *ray_vertex_return {
616            self.types.adjust(ray_vertex_return);
617        }
618
619        if let Some(ref mut external_texture_params) = *external_texture_params {
620            self.types.adjust(external_texture_params);
621        }
622
623        if let Some(ref mut external_texture_transfer_function) =
624            *external_texture_transfer_function
625        {
626            self.types.adjust(external_texture_transfer_function);
627        }
628
629        for handle in predeclared_types.values_mut() {
630            self.types.adjust(handle);
631        }
632    }
633
634    fn adjust_doc_comments(&self, doc_comments: &mut ir::DocComments) {
635        let crate::DocComments {
636            module: _,
637            types: ref mut doc_types,
638            struct_members: ref mut doc_struct_members,
639            entry_points: _,
640            functions: ref mut doc_functions,
641            constants: ref mut doc_constants,
642            global_variables: ref mut doc_globals,
643        } = *doc_comments;
644        log::trace!("adjusting doc comments for types");
645        for (mut ty, doc_comment) in core::mem::take(doc_types) {
646            if !self.types.used(ty) {
647                continue;
648            }
649            self.types.adjust(&mut ty);
650            doc_types.insert(ty, doc_comment);
651        }
652        log::trace!("adjusting doc comments for struct members");
653        for ((mut ty, index), doc_comment) in core::mem::take(doc_struct_members) {
654            if !self.types.used(ty) {
655                continue;
656            }
657            self.types.adjust(&mut ty);
658            doc_struct_members.insert((ty, index), doc_comment);
659        }
660        log::trace!("adjusting doc comments for functions");
661        for (mut handle, doc_comment) in core::mem::take(doc_functions) {
662            if !self.functions.used(handle) {
663                continue;
664            }
665            self.functions.adjust(&mut handle);
666            doc_functions.insert(handle, doc_comment);
667        }
668        log::trace!("adjusting doc comments for constants");
669        for (mut constant, doc_comment) in core::mem::take(doc_constants) {
670            if !self.constants.used(constant) {
671                continue;
672            }
673            self.constants.adjust(&mut constant);
674            doc_constants.insert(constant, doc_comment);
675        }
676        log::trace!("adjusting doc comments for globals");
677        for (mut handle, doc_comment) in core::mem::take(doc_globals) {
678            if !self.globals.used(handle) {
679                continue;
680            }
681            self.globals.adjust(&mut handle);
682            doc_globals.insert(handle, doc_comment);
683        }
684    }
685}
686
687struct FunctionMap {
688    expressions: HandleMap<crate::Expression>,
689}
690
691impl From<FunctionTracer<'_>> for FunctionMap {
692    fn from(used: FunctionTracer) -> Self {
693        FunctionMap {
694            expressions: HandleMap::from_set(used.expressions_used),
695        }
696    }
697}
698
699#[test]
700fn type_expression_interdependence() {
701    let mut module: crate::Module = Default::default();
702    let u32 = module.types.insert(
703        crate::Type {
704            name: None,
705            inner: crate::TypeInner::Scalar(crate::Scalar {
706                kind: crate::ScalarKind::Uint,
707                width: 4,
708            }),
709        },
710        crate::Span::default(),
711    );
712    let expr = module.global_expressions.append(
713        crate::Expression::Literal(crate::Literal::U32(0)),
714        crate::Span::default(),
715    );
716    let type_needs_expression = |module: &mut crate::Module, handle| {
717        let override_handle = module.overrides.append(
718            crate::Override {
719                name: None,
720                id: None,
721                ty: u32,
722                init: Some(handle),
723            },
724            crate::Span::default(),
725        );
726        module.types.insert(
727            crate::Type {
728                name: None,
729                inner: crate::TypeInner::Array {
730                    base: u32,
731                    size: crate::ArraySize::Pending(override_handle),
732                    stride: 4,
733                },
734            },
735            crate::Span::default(),
736        )
737    };
738    let expression_needs_type = |module: &mut crate::Module, handle| {
739        module
740            .global_expressions
741            .append(crate::Expression::ZeroValue(handle), crate::Span::default())
742    };
743    let expression_needs_expression = |module: &mut crate::Module, handle| {
744        module.global_expressions.append(
745            crate::Expression::Load { pointer: handle },
746            crate::Span::default(),
747        )
748    };
749    let type_needs_type = |module: &mut crate::Module, handle| {
750        module.types.insert(
751            crate::Type {
752                name: None,
753                inner: crate::TypeInner::Array {
754                    base: handle,
755                    size: crate::ArraySize::Dynamic,
756                    stride: 0,
757                },
758            },
759            crate::Span::default(),
760        )
761    };
762    let mut type_name_counter = 0;
763    let mut type_needed = |module: &mut crate::Module, handle| {
764        let name = Some(format!("type{type_name_counter}"));
765        type_name_counter += 1;
766        module.types.insert(
767            crate::Type {
768                name,
769                inner: crate::TypeInner::Array {
770                    base: handle,
771                    size: crate::ArraySize::Dynamic,
772                    stride: 0,
773                },
774            },
775            crate::Span::default(),
776        )
777    };
778    let mut override_name_counter = 0;
779    let mut expression_needed = |module: &mut crate::Module, handle| {
780        let name = Some(format!("override{override_name_counter}"));
781        override_name_counter += 1;
782        module.overrides.append(
783            crate::Override {
784                name,
785                id: None,
786                ty: u32,
787                init: Some(handle),
788            },
789            crate::Span::default(),
790        )
791    };
792    let cmp_modules = |mod0: &crate::Module, mod1: &crate::Module| {
793        (mod0.types.iter().collect::<Vec<_>>() == mod1.types.iter().collect::<Vec<_>>())
794            && (mod0.global_expressions.iter().collect::<Vec<_>>()
795                == mod1.global_expressions.iter().collect::<Vec<_>>())
796    };
797    // borrow checker breaks without the tmp variables as of Rust 1.83.0
798    let expr_end = type_needs_expression(&mut module, expr);
799    let ty_trace = type_needs_type(&mut module, expr_end);
800    let expr_init = expression_needs_type(&mut module, ty_trace);
801    expression_needed(&mut module, expr_init);
802    let ty_end = expression_needs_type(&mut module, u32);
803    let expr_trace = expression_needs_expression(&mut module, ty_end);
804    let ty_init = type_needs_expression(&mut module, expr_trace);
805    type_needed(&mut module, ty_init);
806    let untouched = module.clone();
807    compact(&mut module, KeepUnused::Yes);
808    assert!(cmp_modules(&module, &untouched));
809    let unused_expr = module.global_expressions.append(
810        crate::Expression::Literal(crate::Literal::U32(1)),
811        crate::Span::default(),
812    );
813    type_needs_expression(&mut module, unused_expr);
814    assert!(!cmp_modules(&module, &untouched));
815    compact(&mut module, KeepUnused::Yes);
816    assert!(cmp_modules(&module, &untouched));
817}
818
819#[test]
820fn array_length_override() {
821    let mut module: crate::Module = Default::default();
822    let ty_bool = module.types.insert(
823        crate::Type {
824            name: None,
825            inner: crate::TypeInner::Scalar(crate::Scalar::BOOL),
826        },
827        crate::Span::default(),
828    );
829    let ty_u32 = module.types.insert(
830        crate::Type {
831            name: None,
832            inner: crate::TypeInner::Scalar(crate::Scalar::U32),
833        },
834        crate::Span::default(),
835    );
836    let one = module.global_expressions.append(
837        crate::Expression::Literal(crate::Literal::U32(1)),
838        crate::Span::default(),
839    );
840    let _unused_override = module.overrides.append(
841        crate::Override {
842            name: None,
843            id: Some(40),
844            ty: ty_u32,
845            init: None,
846        },
847        crate::Span::default(),
848    );
849    let o = module.overrides.append(
850        crate::Override {
851            name: None,
852            id: Some(42),
853            ty: ty_u32,
854            init: Some(one),
855        },
856        crate::Span::default(),
857    );
858    let _ty_array = module.types.insert(
859        crate::Type {
860            name: Some("array<bool, o>".to_string()),
861            inner: crate::TypeInner::Array {
862                base: ty_bool,
863                size: crate::ArraySize::Pending(o),
864                stride: 4,
865            },
866        },
867        crate::Span::default(),
868    );
869
870    let mut validator = super::valid::Validator::new(
871        super::valid::ValidationFlags::all(),
872        super::valid::Capabilities::all(),
873    );
874
875    assert!(validator.validate(&module).is_ok());
876    compact(&mut module, KeepUnused::Yes);
877    assert!(validator.validate(&module).is_ok());
878}
879
880/// Test mutual references between types and expressions via override
881/// lengths.
882#[test]
883fn array_length_override_mutual() {
884    use crate::Expression as Ex;
885    use crate::Scalar as Sc;
886    use crate::TypeInner as Ti;
887
888    let nowhere = crate::Span::default();
889    let mut module = crate::Module::default();
890    let ty_u32 = module.types.insert(
891        crate::Type {
892            name: None,
893            inner: Ti::Scalar(Sc::U32),
894        },
895        nowhere,
896    );
897
898    // This type is only referred to by the override's init
899    // expression, so if we visit that too early, this type will be
900    // removed incorrectly.
901    let ty_i32 = module.types.insert(
902        crate::Type {
903            name: None,
904            inner: Ti::Scalar(Sc::I32),
905        },
906        nowhere,
907    );
908
909    // An override that the other override's init can refer to.
910    let first_override = module.overrides.append(
911        crate::Override {
912            name: None, // so it is not considered used by definition
913            id: Some(41),
914            ty: ty_i32,
915            init: None,
916        },
917        nowhere,
918    );
919
920    // Initializer expression for the override:
921    //
922    //     (first_override + 0) as u32
923    //
924    // The `first_override` makes it an override expression; the `0`
925    // gets a use of `ty_i32` in there; and the `as` makes it match
926    // the type of `second_override` without actually making
927    // `second_override` point at `ty_i32` directly.
928    let first_override_expr = module
929        .global_expressions
930        .append(Ex::Override(first_override), nowhere);
931    let zero = module
932        .global_expressions
933        .append(Ex::ZeroValue(ty_i32), nowhere);
934    let sum = module.global_expressions.append(
935        Ex::Binary {
936            op: crate::BinaryOperator::Add,
937            left: first_override_expr,
938            right: zero,
939        },
940        nowhere,
941    );
942    let init = module.global_expressions.append(
943        Ex::As {
944            expr: sum,
945            kind: crate::ScalarKind::Uint,
946            convert: None,
947        },
948        nowhere,
949    );
950
951    // Override that serves as the array's length.
952    let second_override = module.overrides.append(
953        crate::Override {
954            name: None, // so it is not considered used by definition
955            id: Some(42),
956            ty: ty_u32,
957            init: Some(init),
958        },
959        nowhere,
960    );
961
962    // Array type that uses the overload as its length.
963    // Since this is named, it is considered used by definition.
964    let _ty_array = module.types.insert(
965        crate::Type {
966            name: Some("delicious_array".to_string()),
967            inner: Ti::Array {
968                base: ty_u32,
969                size: crate::ArraySize::Pending(second_override),
970                stride: 4,
971            },
972        },
973        nowhere,
974    );
975
976    let mut validator = super::valid::Validator::new(
977        super::valid::ValidationFlags::all(),
978        super::valid::Capabilities::all(),
979    );
980
981    assert!(validator.validate(&module).is_ok());
982    compact(&mut module, KeepUnused::Yes);
983    assert!(validator.validate(&module).is_ok());
984}
985
986#[test]
987fn array_length_expression() {
988    let mut module: crate::Module = Default::default();
989    let ty_u32 = module.types.insert(
990        crate::Type {
991            name: None,
992            inner: crate::TypeInner::Scalar(crate::Scalar::U32),
993        },
994        crate::Span::default(),
995    );
996    let _unused_zero = module.global_expressions.append(
997        crate::Expression::Literal(crate::Literal::U32(0)),
998        crate::Span::default(),
999    );
1000    let one = module.global_expressions.append(
1001        crate::Expression::Literal(crate::Literal::U32(1)),
1002        crate::Span::default(),
1003    );
1004    let override_one = module.overrides.append(
1005        crate::Override {
1006            name: None,
1007            id: None,
1008            ty: ty_u32,
1009            init: Some(one),
1010        },
1011        crate::Span::default(),
1012    );
1013    let _ty_array = module.types.insert(
1014        crate::Type {
1015            name: Some("array<u32, 1>".to_string()),
1016            inner: crate::TypeInner::Array {
1017                base: ty_u32,
1018                size: crate::ArraySize::Pending(override_one),
1019                stride: 4,
1020            },
1021        },
1022        crate::Span::default(),
1023    );
1024
1025    let mut validator = super::valid::Validator::new(
1026        super::valid::ValidationFlags::all(),
1027        super::valid::Capabilities::all(),
1028    );
1029
1030    assert!(validator.validate(&module).is_ok());
1031    compact(&mut module, KeepUnused::Yes);
1032    assert!(validator.validate(&module).is_ok());
1033}
1034
1035#[test]
1036fn global_expression_override() {
1037    let mut module: crate::Module = Default::default();
1038    let ty_u32 = module.types.insert(
1039        crate::Type {
1040            name: None,
1041            inner: crate::TypeInner::Scalar(crate::Scalar::U32),
1042        },
1043        crate::Span::default(),
1044    );
1045
1046    // This will only be retained if we trace the initializers
1047    // of overrides referred to by `Expression::Override`
1048    // in global expressions.
1049    let expr1 = module.global_expressions.append(
1050        crate::Expression::Literal(crate::Literal::U32(1)),
1051        crate::Span::default(),
1052    );
1053
1054    // This will only be traced via a global `Expression::Override`.
1055    let o = module.overrides.append(
1056        crate::Override {
1057            name: None,
1058            id: Some(42),
1059            ty: ty_u32,
1060            init: Some(expr1),
1061        },
1062        crate::Span::default(),
1063    );
1064
1065    // This is retained by _p.
1066    let expr2 = module
1067        .global_expressions
1068        .append(crate::Expression::Override(o), crate::Span::default());
1069
1070    // Since this is named, it will be retained.
1071    let _p = module.overrides.append(
1072        crate::Override {
1073            name: Some("p".to_string()),
1074            id: None,
1075            ty: ty_u32,
1076            init: Some(expr2),
1077        },
1078        crate::Span::default(),
1079    );
1080
1081    let mut validator = super::valid::Validator::new(
1082        super::valid::ValidationFlags::all(),
1083        super::valid::Capabilities::all(),
1084    );
1085
1086    assert!(validator.validate(&module).is_ok());
1087    compact(&mut module, KeepUnused::Yes);
1088    assert!(validator.validate(&module).is_ok());
1089}
1090
1091#[test]
1092fn local_expression_override() {
1093    let mut module: crate::Module = Default::default();
1094    let ty_u32 = module.types.insert(
1095        crate::Type {
1096            name: None,
1097            inner: crate::TypeInner::Scalar(crate::Scalar::U32),
1098        },
1099        crate::Span::default(),
1100    );
1101
1102    // This will only be retained if we trace the initializers
1103    // of overrides referred to by `Expression::Override` in a function.
1104    let expr1 = module.global_expressions.append(
1105        crate::Expression::Literal(crate::Literal::U32(1)),
1106        crate::Span::default(),
1107    );
1108
1109    // This will be removed by compaction.
1110    let _unused_override = module.overrides.append(
1111        crate::Override {
1112            name: None,
1113            id: Some(41),
1114            ty: ty_u32,
1115            init: None,
1116        },
1117        crate::Span::default(),
1118    );
1119
1120    // This will only be traced via an `Expression::Override` in a function.
1121    let o = module.overrides.append(
1122        crate::Override {
1123            name: None,
1124            id: Some(42),
1125            ty: ty_u32,
1126            init: Some(expr1),
1127        },
1128        crate::Span::default(),
1129    );
1130
1131    let mut fun = crate::Function {
1132        result: Some(crate::FunctionResult {
1133            ty: ty_u32,
1134            binding: None,
1135        }),
1136        ..crate::Function::default()
1137    };
1138
1139    // This is used by the `Return` statement.
1140    let o_expr = fun
1141        .expressions
1142        .append(crate::Expression::Override(o), crate::Span::default());
1143    fun.body.push(
1144        crate::Statement::Return {
1145            value: Some(o_expr),
1146        },
1147        crate::Span::default(),
1148    );
1149
1150    module.functions.append(fun, crate::Span::default());
1151
1152    let mut validator = super::valid::Validator::new(
1153        super::valid::ValidationFlags::all(),
1154        super::valid::Capabilities::all(),
1155    );
1156
1157    assert!(validator.validate(&module).is_ok());
1158    compact(&mut module, KeepUnused::Yes);
1159    assert!(validator.validate(&module).is_ok());
1160}
1161
1162#[test]
1163fn unnamed_constant_type() {
1164    let mut module = crate::Module::default();
1165    let nowhere = crate::Span::default();
1166
1167    // This type is used only by the unnamed constant.
1168    let ty_u32 = module.types.insert(
1169        crate::Type {
1170            name: None,
1171            inner: crate::TypeInner::Scalar(crate::Scalar::U32),
1172        },
1173        nowhere,
1174    );
1175
1176    // This type is used by the named constant.
1177    let ty_vec_u32 = module.types.insert(
1178        crate::Type {
1179            name: None,
1180            inner: crate::TypeInner::Vector {
1181                size: crate::VectorSize::Bi,
1182                scalar: crate::Scalar::U32,
1183            },
1184        },
1185        nowhere,
1186    );
1187
1188    let unnamed_init = module
1189        .global_expressions
1190        .append(crate::Expression::Literal(crate::Literal::U32(0)), nowhere);
1191
1192    let unnamed_constant = module.constants.append(
1193        crate::Constant {
1194            name: None,
1195            ty: ty_u32,
1196            init: unnamed_init,
1197        },
1198        nowhere,
1199    );
1200
1201    // The named constant is initialized using a Splat expression, to
1202    // give the named constant a type distinct from the unnamed
1203    // constant's.
1204    let unnamed_constant_expr = module
1205        .global_expressions
1206        .append(crate::Expression::Constant(unnamed_constant), nowhere);
1207    let named_init = module.global_expressions.append(
1208        crate::Expression::Splat {
1209            size: crate::VectorSize::Bi,
1210            value: unnamed_constant_expr,
1211        },
1212        nowhere,
1213    );
1214
1215    let _named_constant = module.constants.append(
1216        crate::Constant {
1217            name: Some("totally_named".to_string()),
1218            ty: ty_vec_u32,
1219            init: named_init,
1220        },
1221        nowhere,
1222    );
1223
1224    let mut validator = super::valid::Validator::new(
1225        super::valid::ValidationFlags::all(),
1226        super::valid::Capabilities::all(),
1227    );
1228
1229    assert!(validator.validate(&module).is_ok());
1230    compact(&mut module, KeepUnused::Yes);
1231    assert!(validator.validate(&module).is_ok());
1232}
1233
1234#[test]
1235fn unnamed_override_type() {
1236    let mut module = crate::Module::default();
1237    let nowhere = crate::Span::default();
1238
1239    // This type is used only by the unnamed override.
1240    let ty_u32 = module.types.insert(
1241        crate::Type {
1242            name: None,
1243            inner: crate::TypeInner::Scalar(crate::Scalar::U32),
1244        },
1245        nowhere,
1246    );
1247
1248    // This type is used by the named override.
1249    let ty_i32 = module.types.insert(
1250        crate::Type {
1251            name: None,
1252            inner: crate::TypeInner::Scalar(crate::Scalar::I32),
1253        },
1254        nowhere,
1255    );
1256
1257    let unnamed_init = module
1258        .global_expressions
1259        .append(crate::Expression::Literal(crate::Literal::U32(0)), nowhere);
1260
1261    let unnamed_override = module.overrides.append(
1262        crate::Override {
1263            name: None,
1264            id: Some(42),
1265            ty: ty_u32,
1266            init: Some(unnamed_init),
1267        },
1268        nowhere,
1269    );
1270
1271    // The named override is initialized using a Splat expression, to
1272    // give the named override a type distinct from the unnamed
1273    // override's.
1274    let unnamed_override_expr = module
1275        .global_expressions
1276        .append(crate::Expression::Override(unnamed_override), nowhere);
1277    let named_init = module.global_expressions.append(
1278        crate::Expression::As {
1279            expr: unnamed_override_expr,
1280            kind: crate::ScalarKind::Sint,
1281            convert: None,
1282        },
1283        nowhere,
1284    );
1285
1286    let _named_override = module.overrides.append(
1287        crate::Override {
1288            name: Some("totally_named".to_string()),
1289            id: None,
1290            ty: ty_i32,
1291            init: Some(named_init),
1292        },
1293        nowhere,
1294    );
1295
1296    let mut validator = super::valid::Validator::new(
1297        super::valid::ValidationFlags::all(),
1298        super::valid::Capabilities::all(),
1299    );
1300
1301    assert!(validator.validate(&module).is_ok());
1302    compact(&mut module, KeepUnused::Yes);
1303    assert!(validator.validate(&module).is_ok());
1304}