naga/compact/
mod.rs

1mod expressions;
2mod functions;
3mod handle_set_map;
4mod statements;
5mod types;
6
7use crate::{arena, compact::functions::FunctionTracer};
8use handle_set_map::{HandleMap, HandleSet};
9
10/// Remove unused types, expressions, and constants from `module`.
11///
12/// Assuming that all globals, named constants, special types,
13/// functions and entry points in `module` are used, determine which
14/// types, constants, and expressions (both function-local and global
15/// constant expressions) are actually used, and remove the rest,
16/// adjusting all handles as necessary. The result should be a module
17/// functionally identical to the original.
18///
19/// This may be useful to apply to modules generated in the snapshot
20/// tests. Our backends often generate temporary names based on handle
21/// indices, which means that adding or removing unused arena entries
22/// can affect the output even though they have no semantic effect.
23/// Such meaningless changes add noise to snapshot diffs, making
24/// accurate patch review difficult. Compacting the modules before
25/// generating snapshots makes the output independent of unused arena
26/// entries.
27///
28/// # Panics
29///
30/// If `module` has not passed validation, this may panic.
31pub fn compact(module: &mut crate::Module) {
32    let mut module_tracer = ModuleTracer::new(module);
33
34    // We treat all globals as used by definition.
35    log::trace!("tracing global variables");
36    {
37        for (_, global) in module.global_variables.iter() {
38            log::trace!("tracing global {:?}", global.name);
39            module_tracer.types_used.insert(global.ty);
40            if let Some(init) = global.init {
41                module_tracer.global_expressions_used.insert(init);
42            }
43        }
44    }
45
46    // We treat all special types as used by definition.
47    module_tracer.trace_special_types(&module.special_types);
48
49    // We treat all named constants as used by definition.
50    for (handle, constant) in module.constants.iter() {
51        if constant.name.is_some() {
52            module_tracer.constants_used.insert(handle);
53            module_tracer.global_expressions_used.insert(constant.init);
54        }
55    }
56
57    // We treat all overrides as used by definition.
58    for (_, override_) in module.overrides.iter() {
59        module_tracer.types_used.insert(override_.ty);
60        if let Some(init) = override_.init {
61            module_tracer.global_expressions_used.insert(init);
62        }
63    }
64
65    // We assume that all functions are used.
66    //
67    // Observe which types, constant expressions, constants, and
68    // expressions each function uses, and produce maps for each
69    // function from pre-compaction to post-compaction expression
70    // handles.
71    log::trace!("tracing functions");
72    let function_maps: Vec<FunctionMap> = module
73        .functions
74        .iter()
75        .map(|(_, f)| {
76            log::trace!("tracing function {:?}", f.name);
77            let mut function_tracer = module_tracer.as_function(f);
78            function_tracer.trace();
79            FunctionMap::from(function_tracer)
80        })
81        .collect();
82
83    // Similarly, observe what each entry point actually uses.
84    log::trace!("tracing entry points");
85    let entry_point_maps: Vec<FunctionMap> = module
86        .entry_points
87        .iter()
88        .map(|e| {
89            log::trace!("tracing entry point {:?}", e.function.name);
90            let mut used = module_tracer.as_function(&e.function);
91            used.trace();
92            FunctionMap::from(used)
93        })
94        .collect();
95
96    // Given that the above steps have marked all the constant
97    // expressions used directly by globals, constants, functions, and
98    // entry points, walk the constant expression arena to find all
99    // constant expressions used, directly or indirectly.
100    module_tracer.as_const_expression().trace_expressions();
101
102    // Constants' initializers are taken care of already, because
103    // expression tracing sees through constants. But we still need to
104    // note type usage.
105    for (handle, constant) in module.constants.iter() {
106        if module_tracer.constants_used.contains(handle) {
107            module_tracer.types_used.insert(constant.ty);
108        }
109    }
110
111    // Treat all named types as used.
112    for (handle, ty) in module.types.iter() {
113        log::trace!("tracing type {:?}, name {:?}", handle, ty.name);
114        if ty.name.is_some() {
115            module_tracer.types_used.insert(handle);
116        }
117    }
118
119    // Propagate usage through types.
120    module_tracer.as_type().trace_types();
121
122    // Now that we know what is used and what is never touched,
123    // produce maps from the `Handle`s that appear in `module` now to
124    // the corresponding `Handle`s that will refer to the same items
125    // in the compacted module.
126    let module_map = ModuleMap::from(module_tracer);
127
128    // Drop unused types from the type arena.
129    //
130    // `FastIndexSet`s don't have an underlying Vec<T> that we can
131    // steal, compact in place, and then rebuild the `FastIndexSet`
132    // from. So we have to rebuild the type arena from scratch.
133    log::trace!("compacting types");
134    let mut new_types = arena::UniqueArena::new();
135    for (old_handle, mut ty, span) in module.types.drain_all() {
136        if let Some(expected_new_handle) = module_map.types.try_adjust(old_handle) {
137            module_map.adjust_type(&mut ty);
138            let actual_new_handle = new_types.insert(ty, span);
139            assert_eq!(actual_new_handle, expected_new_handle);
140        }
141    }
142    module.types = new_types;
143    log::trace!("adjusting special types");
144    module_map.adjust_special_types(&mut module.special_types);
145
146    // Drop unused constant expressions, reusing existing storage.
147    log::trace!("adjusting constant expressions");
148    module.global_expressions.retain_mut(|handle, expr| {
149        if module_map.global_expressions.used(handle) {
150            module_map.adjust_expression(expr, &module_map.global_expressions);
151            true
152        } else {
153            false
154        }
155    });
156
157    // Drop unused constants in place, reusing existing storage.
158    log::trace!("adjusting constants");
159    module.constants.retain_mut(|handle, constant| {
160        if module_map.constants.used(handle) {
161            module_map.types.adjust(&mut constant.ty);
162            module_map.global_expressions.adjust(&mut constant.init);
163            true
164        } else {
165            false
166        }
167    });
168
169    // Adjust override types and initializers.
170    log::trace!("adjusting overrides");
171    for (_, override_) in module.overrides.iter_mut() {
172        module_map.types.adjust(&mut override_.ty);
173        if let Some(init) = override_.init.as_mut() {
174            module_map.global_expressions.adjust(init);
175        }
176    }
177
178    // Adjust global variables' types and initializers.
179    log::trace!("adjusting global variables");
180    for (_, global) in module.global_variables.iter_mut() {
181        log::trace!("adjusting global {:?}", global.name);
182        module_map.types.adjust(&mut global.ty);
183        if let Some(ref mut init) = global.init {
184            module_map.global_expressions.adjust(init);
185        }
186    }
187
188    // Temporary storage to help us reuse allocations of existing
189    // named expression tables.
190    let mut reused_named_expressions = crate::NamedExpressions::default();
191
192    // Compact each function.
193    for ((_, function), map) in module.functions.iter_mut().zip(function_maps.iter()) {
194        log::trace!("compacting function {:?}", function.name);
195        map.compact(function, &module_map, &mut reused_named_expressions);
196    }
197
198    // Compact each entry point.
199    for (entry, map) in module.entry_points.iter_mut().zip(entry_point_maps.iter()) {
200        log::trace!("compacting entry point {:?}", entry.function.name);
201        map.compact(
202            &mut entry.function,
203            &module_map,
204            &mut reused_named_expressions,
205        );
206    }
207}
208
209struct ModuleTracer<'module> {
210    module: &'module crate::Module,
211    types_used: HandleSet<crate::Type>,
212    constants_used: HandleSet<crate::Constant>,
213    global_expressions_used: HandleSet<crate::Expression>,
214}
215
216impl<'module> ModuleTracer<'module> {
217    fn new(module: &'module crate::Module) -> Self {
218        Self {
219            module,
220            types_used: HandleSet::for_arena(&module.types),
221            constants_used: HandleSet::for_arena(&module.constants),
222            global_expressions_used: HandleSet::for_arena(&module.global_expressions),
223        }
224    }
225
226    fn trace_special_types(&mut self, special_types: &crate::SpecialTypes) {
227        let crate::SpecialTypes {
228            ref ray_desc,
229            ref ray_intersection,
230            ref predeclared_types,
231        } = *special_types;
232
233        if let Some(ray_desc) = *ray_desc {
234            self.types_used.insert(ray_desc);
235        }
236        if let Some(ray_intersection) = *ray_intersection {
237            self.types_used.insert(ray_intersection);
238        }
239        for (_, &handle) in predeclared_types {
240            self.types_used.insert(handle);
241        }
242    }
243
244    fn as_type(&mut self) -> types::TypeTracer {
245        types::TypeTracer {
246            types: &self.module.types,
247            types_used: &mut self.types_used,
248        }
249    }
250
251    fn as_const_expression(&mut self) -> expressions::ExpressionTracer {
252        expressions::ExpressionTracer {
253            expressions: &self.module.global_expressions,
254            constants: &self.module.constants,
255            overrides: &self.module.overrides,
256            types_used: &mut self.types_used,
257            constants_used: &mut self.constants_used,
258            expressions_used: &mut self.global_expressions_used,
259            global_expressions_used: None,
260        }
261    }
262
263    pub fn as_function<'tracer>(
264        &'tracer mut self,
265        function: &'tracer crate::Function,
266    ) -> FunctionTracer<'tracer> {
267        FunctionTracer {
268            function,
269            constants: &self.module.constants,
270            overrides: &self.module.overrides,
271            types_used: &mut self.types_used,
272            constants_used: &mut self.constants_used,
273            global_expressions_used: &mut self.global_expressions_used,
274            expressions_used: HandleSet::for_arena(&function.expressions),
275        }
276    }
277}
278
279struct ModuleMap {
280    types: HandleMap<crate::Type>,
281    constants: HandleMap<crate::Constant>,
282    global_expressions: HandleMap<crate::Expression>,
283}
284
285impl From<ModuleTracer<'_>> for ModuleMap {
286    fn from(used: ModuleTracer) -> Self {
287        ModuleMap {
288            types: HandleMap::from_set(used.types_used),
289            constants: HandleMap::from_set(used.constants_used),
290            global_expressions: HandleMap::from_set(used.global_expressions_used),
291        }
292    }
293}
294
295impl ModuleMap {
296    fn adjust_special_types(&self, special: &mut crate::SpecialTypes) {
297        let crate::SpecialTypes {
298            ref mut ray_desc,
299            ref mut ray_intersection,
300            ref mut predeclared_types,
301        } = *special;
302
303        if let Some(ref mut ray_desc) = *ray_desc {
304            self.types.adjust(ray_desc);
305        }
306        if let Some(ref mut ray_intersection) = *ray_intersection {
307            self.types.adjust(ray_intersection);
308        }
309
310        for handle in predeclared_types.values_mut() {
311            self.types.adjust(handle);
312        }
313    }
314}
315
316struct FunctionMap {
317    expressions: HandleMap<crate::Expression>,
318}
319
320impl From<FunctionTracer<'_>> for FunctionMap {
321    fn from(used: FunctionTracer) -> Self {
322        FunctionMap {
323            expressions: HandleMap::from_set(used.expressions_used),
324        }
325    }
326}