Skip to main content

i_slint_compiler/llr/optim_passes/
remove_unused.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::llr::*;
5use typed_index_collections::TiVec;
6
7struct Mapping {
8    prop_mapping: TiVec<PropertyIdx, Option<PropertyIdx>>,
9    callback_mapping: TiVec<CallbackIdx, Option<CallbackIdx>>,
10}
11
12impl Mapping {
13    fn keep(&self, member: &LocalMemberIndex) -> bool {
14        match member {
15            LocalMemberIndex::Property(p) => self.prop_mapping[*p].is_some(),
16            LocalMemberIndex::Callback(c) => self.callback_mapping[*c].is_some(),
17            _ => true,
18        }
19    }
20}
21
22type ScMappings = TiVec<SubComponentIdx, Mapping>;
23type GlobMappings = TiVec<GlobalIdx, Mapping>;
24
25pub fn remove_unused(root: &mut CompilationUnit) {
26    struct RemoveUnusedMappings {
27        sc_mappings: ScMappings,
28        glob_mappings: GlobMappings,
29    }
30    let mappings = RemoveUnusedMappings {
31        sc_mappings: root
32            .sub_components
33            .iter_mut()
34            .map(|sc| create_mapping(&mut sc.properties, &mut sc.callbacks))
35            .collect(),
36        glob_mappings: root
37            .globals
38            .iter_mut()
39            .map(|g| {
40                clean_vec(&mut g.const_properties, &g.properties);
41                clean_vec(&mut g.prop_analysis, &g.properties);
42                create_mapping(&mut g.properties, &mut g.callbacks)
43            })
44            .collect(),
45    };
46
47    let state = visitor::VisitorState::new(root);
48
49    for (idx, sc) in root.sub_components.iter_mut_enumerated() {
50        let keep = |refer: &MemberReference| match refer {
51            MemberReference::Relative { parent_level, local_reference } => {
52                assert_eq!(*parent_level, 0);
53                let idx = state.follow_sub_components(idx, &local_reference.sub_component_path);
54                mappings.sc_mappings[idx].keep(&local_reference.reference)
55            }
56            MemberReference::Global { global_index, member } => {
57                mappings.glob_mappings[*global_index].keep(member)
58            }
59        };
60
61        let mut property_init_mapping = Vec::new();
62        let mut i = 0;
63        sc.property_init.retain(|(x, v)| {
64            if keep(x) && v.use_count.get() > 0 {
65                property_init_mapping.push(Some(i));
66                i += 1;
67                true
68            } else {
69                property_init_mapping.push(None);
70                false
71            }
72        });
73        sc.change_callbacks.retain(|(x, _)| keep(x));
74        sc.const_properties.retain(|x| {
75            let idx = state.follow_sub_components(idx, &x.sub_component_path);
76            mappings.sc_mappings[idx].keep(&x.reference)
77        });
78        sc.prop_analysis.retain(|x, v| {
79            v.property_init = v.property_init.and_then(|x| property_init_mapping[x]);
80            keep(x)
81        });
82        sc.animations.retain(|x, _| keep(&x.clone().into()));
83    }
84    for (idx, g) in root.globals.iter_mut_enumerated() {
85        g.init_values.retain(|x, _| mappings.glob_mappings[idx].keep(x));
86    }
87
88    impl visitor::Visitor for &RemoveUnusedMappings {
89        fn visit_property_idx(
90            &mut self,
91            p: &mut PropertyIdx,
92            scope: &EvaluationScope,
93            _state: &visitor::VisitorState,
94        ) {
95            match scope {
96                EvaluationScope::SubComponent(sub_component_idx, _) => {
97                    // Debugging hint: if this unwrap fails, check if count_property_use() didn't
98                    // forget to visit something, leading to the property being removed
99                    *p = self.sc_mappings[*sub_component_idx].prop_mapping[*p].unwrap();
100                }
101                EvaluationScope::Global(global_idx) => {
102                    *p = self.glob_mappings[*global_idx].prop_mapping[*p].unwrap();
103                }
104            }
105        }
106
107        fn visit_callback_idx(
108            &mut self,
109            p: &mut CallbackIdx,
110            scope: &EvaluationScope,
111            _state: &visitor::VisitorState,
112        ) {
113            match scope {
114                EvaluationScope::SubComponent(sub_component_idx, _) => {
115                    *p = self.sc_mappings[*sub_component_idx].callback_mapping[*p].unwrap();
116                }
117                EvaluationScope::Global(global_idx) => {
118                    *p = self.glob_mappings[*global_idx].callback_mapping[*p].unwrap();
119                }
120            }
121        }
122    }
123    let mut visitor = &mappings;
124    visitor::visit_compilation_unit(root, &state, &mut visitor);
125}
126
127fn create_mapping(
128    properties: &mut TiVec<PropertyIdx, Property>,
129    callbacks: &mut TiVec<CallbackIdx, Callback>,
130) -> Mapping {
131    Mapping {
132        prop_mapping: create_vec_mapping(properties, |p| p.use_count.get() > 0),
133        callback_mapping: create_vec_mapping(callbacks, |c| c.use_count.get() > 0),
134    }
135}
136
137fn create_vec_mapping<Idx: From<usize>, T>(
138    vec: &mut TiVec<Idx, T>,
139    mut retain: impl FnMut(&T) -> bool,
140) -> TiVec<Idx, Option<Idx>> {
141    let mut map = TiVec::with_capacity(vec.len());
142    let mut i = 0;
143    vec.retain(|t| {
144        if retain(t) {
145            map.push(Some(Idx::from(i)));
146            i += 1;
147            true
148        } else {
149            map.push(None);
150            false
151        }
152    });
153    map
154}
155
156fn clean_vec<T>(vec: &mut TiVec<PropertyIdx, T>, properties: &TiVec<PropertyIdx, Property>) {
157    let mut idx = 0;
158    vec.retain(|_| {
159        idx += 1;
160        properties[PropertyIdx::from(idx - 1)].use_count.get() >= 1
161    });
162}
163
164mod visitor {
165
166    use super::*;
167
168    pub trait Visitor {
169        fn visit_property_idx(
170            &mut self,
171            _p: &mut PropertyIdx,
172            _scope: &EvaluationScope,
173            _state: &VisitorState,
174        ) {
175        }
176        fn visit_function_idx(
177            &mut self,
178            _p: &mut FunctionIdx,
179            _scope: &EvaluationScope,
180            _state: &VisitorState,
181        ) {
182        }
183
184        fn visit_callback_idx(
185            &mut self,
186            _p: &mut CallbackIdx,
187            _scope: &EvaluationScope,
188            _state: &VisitorState,
189        ) {
190        }
191    }
192
193    pub struct VisitorState {
194        /// Copy of SubComponent::sub_components::ty
195        sub_component_maps: TiVec<SubComponentIdx, TiVec<SubComponentInstanceIdx, SubComponentIdx>>,
196        /// parent mapping
197        parent_mapping: TiVec<SubComponentIdx, Option<SubComponentIdx>>,
198    }
199
200    impl VisitorState {
201        pub fn new(cu: &CompilationUnit) -> Self {
202            let mut parent_mapping = TiVec::new();
203            parent_mapping.resize(cu.sub_components.len(), None);
204            for (idx, sc) in cu.sub_components.iter_enumerated() {
205                for r in &sc.repeated {
206                    parent_mapping[r.sub_tree.root] = Some(idx);
207                }
208                for p in &sc.popup_windows {
209                    parent_mapping[p.item_tree.root] = Some(idx);
210                }
211                for m in &sc.menu_item_trees {
212                    parent_mapping[m.root] = Some(idx);
213                }
214            }
215            Self {
216                sub_component_maps: cu
217                    .sub_components
218                    .iter()
219                    .map(|sc| sc.sub_components.iter().map(|x| x.ty).collect())
220                    .collect(),
221                parent_mapping,
222            }
223        }
224
225        pub fn follow_sub_components(
226            &self,
227            mut sc: SubComponentIdx,
228            sub_component_path: &[SubComponentInstanceIdx],
229        ) -> SubComponentIdx {
230            for i in sub_component_path {
231                sc = self.sub_component_maps[sc][*i];
232            }
233            sc
234        }
235    }
236
237    pub fn visit_compilation_unit(
238        CompilationUnit {
239            public_components,
240            sub_components,
241            used_sub_components: _,
242            globals,
243            popup_menu,
244            has_debug_info: _,
245            #[cfg(feature = "bundle-translations")]
246                translations: _,
247        }: &mut crate::llr::CompilationUnit,
248        state: &VisitorState,
249        visitor: &mut (impl Visitor + ?Sized),
250    ) {
251        for c in public_components {
252            visit_public_component(c, state, visitor);
253        }
254        for (idx, sc) in sub_components.iter_mut_enumerated() {
255            visit_sub_component(idx, sc, state, visitor);
256        }
257        for (idx, g) in globals.iter_mut_enumerated() {
258            visit_global(idx, g, state, visitor);
259        }
260        if let Some(p) = popup_menu {
261            visit_popup_menu(p, state, visitor);
262        }
263    }
264
265    pub fn visit_public_component(
266        PublicComponent {
267            public_properties,
268            private_properties: _,
269            item_tree,
270            name: _,
271            top_level_type: _,
272        }: &mut PublicComponent,
273        state: &VisitorState,
274        visitor: &mut (impl Visitor + ?Sized),
275    ) {
276        let scope = EvaluationScope::SubComponent(item_tree.root, None);
277        for p in public_properties {
278            visit_public_property(p, &scope, state, visitor);
279        }
280    }
281
282    pub fn visit_sub_component(
283        idx: SubComponentIdx,
284        SubComponent {
285            name: _,
286            properties: _,
287            callbacks: _,
288            functions,
289            items: _,
290            repeated,
291            component_containers: _,
292            popup_windows,
293            menu_item_trees: _,
294            timers,
295            sub_components: _,
296            property_init,
297            change_callbacks,
298            animations,
299            two_way_bindings,
300            const_properties,
301            pre_init_code,
302            init_code,
303            geometries,
304            layout_info_h,
305            layout_info_v,
306            child_of_layout: _,
307            grid_layout_input_for_repeated,
308            flexbox_layout_item_info_for_repeated,
309            is_repeated_row: _,
310            grid_layout_children,
311            accessible_prop,
312            element_infos: _,
313            row_child_templates: _,
314            prop_analysis,
315        }: &mut SubComponent,
316        state: &VisitorState,
317        visitor: &mut (impl Visitor + ?Sized),
318    ) {
319        let scope = EvaluationScope::SubComponent(idx, None);
320        for f in functions {
321            visit_function(f, &scope, state, visitor);
322        }
323        for RepeatedElement {
324            model,
325            index_prop,
326            data_prop,
327            sub_tree,
328            index_in_tree: _,
329            listview,
330            container_item_index: _,
331        } in repeated
332        {
333            visit_expression(model.get_mut(), &scope, state, visitor);
334            let inner_scope = EvaluationScope::SubComponent(sub_tree.root, None);
335            if let Some(index_prop) = index_prop {
336                visitor.visit_property_idx(index_prop, &inner_scope, state);
337            }
338            if let Some(data_prop) = data_prop {
339                visitor.visit_property_idx(data_prop, &inner_scope, state);
340            }
341
342            if let Some(listview) = listview {
343                visit_member_reference(&mut listview.viewport_y, &scope, state, visitor);
344                visit_member_reference(&mut listview.viewport_height, &scope, state, visitor);
345                visit_member_reference(&mut listview.viewport_width, &scope, state, visitor);
346                visit_member_reference(&mut listview.listview_width, &scope, state, visitor);
347                visit_member_reference(&mut listview.listview_height, &scope, state, visitor);
348
349                visit_member_reference(&mut listview.prop_y, &inner_scope, state, visitor);
350                visit_member_reference(&mut listview.prop_height, &inner_scope, state, visitor);
351            }
352        }
353
354        for p in popup_windows {
355            let popup_scope = EvaluationScope::SubComponent(p.item_tree.root, None);
356            visit_expression(p.position.get_mut(), &popup_scope, state, visitor);
357        }
358        for t in timers {
359            visit_expression(t.interval.get_mut(), &scope, state, visitor);
360            visit_expression(t.triggered.get_mut(), &scope, state, visitor);
361            visit_expression(t.running.get_mut(), &scope, state, visitor);
362        }
363        for (idx, init) in property_init {
364            visit_member_reference(idx, &scope, state, visitor);
365            visit_binding_expression(init, &scope, state, visitor);
366        }
367        for (idx, e) in change_callbacks {
368            visit_member_reference(idx, &scope, state, visitor);
369            visit_expression(e.get_mut(), &scope, state, visitor);
370        }
371        *animations = std::mem::take(animations)
372            .into_iter()
373            .map(|(mut k, mut v)| {
374                visit_local_member_reference(&mut k, &scope, state, visitor);
375                visit_expression(&mut v, &scope, state, visitor);
376                (k, v)
377            })
378            .collect();
379
380        for twb in two_way_bindings {
381            visit_local_member_reference(&mut twb.prop1, &scope, state, visitor);
382            visit_member_reference(&mut twb.prop2, &scope, state, visitor);
383        }
384        for c in const_properties {
385            visit_local_member_reference(c, &scope, state, visitor);
386        }
387        for i in pre_init_code.iter_mut().chain(init_code) {
388            visit_expression(i.get_mut(), &scope, state, visitor);
389        }
390        for g in geometries.iter_mut().flatten() {
391            visit_expression(g.get_mut(), &scope, state, visitor);
392        }
393        visit_expression(layout_info_h.get_mut(), &scope, state, visitor);
394        visit_expression(layout_info_v.get_mut(), &scope, state, visitor);
395        if let Some(e) = grid_layout_input_for_repeated {
396            visit_expression(e.get_mut(), &scope, state, visitor);
397        }
398        if let Some(e) = flexbox_layout_item_info_for_repeated {
399            visit_expression(e.get_mut(), &scope, state, visitor);
400        }
401        for child in grid_layout_children {
402            visit_expression(child.layout_info_h.get_mut(), &scope, state, visitor);
403            visit_expression(child.layout_info_v.get_mut(), &scope, state, visitor);
404        }
405
406        for a in accessible_prop.values_mut() {
407            visit_expression(a.get_mut(), &scope, state, visitor);
408        }
409
410        *prop_analysis = std::mem::take(prop_analysis)
411            .into_iter()
412            .map(|(mut k, v)| {
413                visit_member_reference(&mut k, &scope, state, visitor);
414                (k, v)
415            })
416            .collect();
417    }
418
419    fn visit_global(
420        global_idx: GlobalIdx,
421        GlobalComponent {
422            name: _,
423            properties: _,
424            callbacks: _,
425            functions,
426            init_values,
427            change_callbacks,
428            const_properties: _,
429            public_properties,
430            private_properties: _,
431            exported: _,
432            aliases: _,
433            is_builtin: _,
434            from_library: _,
435            prop_analysis: _,
436        }: &mut GlobalComponent,
437        state: &VisitorState,
438        visitor: &mut (impl Visitor + ?Sized),
439    ) {
440        let scope = EvaluationScope::Global(global_idx);
441        for f in functions {
442            visit_function(f, &scope, state, visitor);
443        }
444
445        *init_values = std::mem::take(init_values)
446            .into_iter()
447            .map(|(mut k, mut v)| {
448                visit_member_index(&mut k, &scope, state, visitor);
449                visit_binding_expression(&mut v, &scope, state, visitor);
450                (k, v)
451            })
452            .collect();
453
454        *change_callbacks = std::mem::take(change_callbacks)
455            .into_iter()
456            .map(|(mut k, mut v)| {
457                visitor.visit_property_idx(&mut k, &scope, state);
458                visit_expression(v.get_mut(), &scope, state, visitor);
459                (k, v)
460            })
461            .collect();
462
463        for p in public_properties {
464            visit_public_property(p, &scope, state, visitor);
465        }
466    }
467
468    pub fn visit_popup_menu(
469        PopupMenu { item_tree, sub_menu, activated, close, entries }: &mut PopupMenu,
470        state: &VisitorState,
471        visitor: &mut (impl Visitor + ?Sized),
472    ) {
473        let scope = EvaluationScope::SubComponent(item_tree.root, None);
474        visit_member_reference(sub_menu, &scope, state, visitor);
475        visit_member_reference(activated, &scope, state, visitor);
476        visit_member_reference(close, &scope, state, visitor);
477        visit_member_reference(entries, &scope, state, visitor);
478    }
479
480    pub fn visit_public_property(
481        PublicProperty { name: _, ty: _, prop, read_only: _ }: &mut PublicProperty,
482        scope: &EvaluationScope,
483        state: &VisitorState,
484        visitor: &mut (impl Visitor + ?Sized),
485    ) {
486        visit_member_reference(prop, scope, state, visitor);
487    }
488
489    pub fn visit_function(
490        Function { name: _, ret_ty: _, args: _, code }: &mut Function,
491        scope: &EvaluationScope,
492        state: &VisitorState,
493        visitor: &mut (impl Visitor + ?Sized),
494    ) {
495        visit_expression(code, scope, state, visitor);
496    }
497
498    pub fn visit_expression(
499        expr: &mut Expression,
500        scope: &EvaluationScope,
501        state: &VisitorState,
502        visitor: &mut (impl Visitor + ?Sized),
503    ) {
504        expr.visit_recursive_mut(&mut |expr| {
505            let p = match expr {
506                Expression::PropertyReference(p) => p,
507                Expression::CallBackCall { callback, .. } => callback,
508                Expression::PropertyAssignment { property, .. } => property,
509                Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
510                Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
511                _ => return,
512            };
513            visit_member_reference(p, scope, state, visitor);
514        });
515    }
516
517    pub fn visit_binding_expression(
518        BindingExpression { expression, animation, is_constant: _, is_state_info: _, use_count: _ }: &mut BindingExpression,
519        scope: &EvaluationScope,
520        state: &VisitorState,
521        visitor: &mut (impl Visitor + ?Sized),
522    ) {
523        visit_expression(expression.get_mut(), scope, state, visitor);
524        match animation {
525            Some(Animation::Static(anim) | Animation::Transition(anim)) => {
526                visit_expression(anim, scope, state, visitor)
527            }
528            None => (),
529        }
530    }
531
532    pub fn visit_member_reference(
533        member: &mut MemberReference,
534        scope: &EvaluationScope,
535        state: &VisitorState,
536        visitor: &mut (impl Visitor + ?Sized),
537    ) {
538        match member {
539            MemberReference::Relative { parent_level, local_reference } => {
540                let &EvaluationScope::SubComponent(mut sc, _) = scope else { unreachable!() };
541                for _ in 0..*parent_level {
542                    sc = state.parent_mapping[sc].unwrap();
543                }
544                let scope = EvaluationScope::SubComponent(sc, None);
545                visit_local_member_reference(local_reference, &scope, state, visitor);
546            }
547            MemberReference::Global { global_index, member } => {
548                let scope = EvaluationScope::Global(*global_index);
549                visit_member_index(member, &scope, state, visitor);
550            }
551        }
552    }
553
554    pub fn visit_local_member_reference(
555        local_reference: &mut LocalMemberReference,
556        scope: &EvaluationScope,
557        state: &VisitorState,
558        visitor: &mut (impl Visitor + ?Sized),
559    ) {
560        let scope = match scope {
561            EvaluationScope::SubComponent(sub_component_idx, _) => EvaluationScope::SubComponent(
562                state
563                    .follow_sub_components(*sub_component_idx, &local_reference.sub_component_path),
564                None,
565            ),
566            scope => *scope,
567        };
568        visit_member_index(&mut local_reference.reference, &scope, state, visitor);
569    }
570
571    pub fn visit_member_index(
572        member: &mut LocalMemberIndex,
573        scope: &EvaluationScope,
574        state: &VisitorState,
575        visitor: &mut (impl Visitor + ?Sized),
576    ) {
577        match member {
578            LocalMemberIndex::Property(p) => {
579                visitor.visit_property_idx(p, scope, state);
580            }
581            LocalMemberIndex::Function(f) => {
582                visitor.visit_function_idx(f, scope, state);
583            }
584            LocalMemberIndex::Callback(c) => {
585                visitor.visit_callback_idx(c, scope, state);
586            }
587            LocalMemberIndex::Native { .. } => {}
588        }
589    }
590}