i_slint_compiler/passes/
deduplicate_property_read.rs1use crate::expression_tree::*;
7use crate::langtype::Type;
8use crate::object_tree::*;
9use smol_str::{SmolStr, format_smolstr};
10use std::cell::RefCell;
11use std::collections::{BTreeMap, HashMap};
12
13pub fn deduplicate_property_read(component: &Component) {
14 visit_all_expressions(component, |expr, ty| {
15 if matches!(ty(), Type::Callback { .. }) {
16 return;
18 }
19 if !super::purity_check::is_pure(expr) {
22 return;
23 }
24 process_expression(expr, &DedupPropState::default());
25 });
26}
27
28struct ReadCount {
29 count: usize,
30 has_been_mapped: bool,
31}
32
33#[derive(Default)]
34struct PropertyReadCounts {
35 counts: HashMap<NamedReference, ReadCount>,
36 has_duplicate: bool,
38}
39
40#[derive(Default)]
41struct DedupPropState<'a> {
42 parent_state: Option<&'a DedupPropState<'a>>,
43 counts: RefCell<PropertyReadCounts>,
44}
45
46impl DedupPropState<'_> {
47 fn add(&self, nr: &NamedReference) {
48 if self.parent_state.is_some_and(|pc| pc.add_from_children(nr)) {
49 return;
50 }
51 let mut use_counts = self.counts.borrow_mut();
52 let use_counts = &mut *use_counts;
53 let has_duplicate = &mut use_counts.has_duplicate;
54 use_counts
55 .counts
56 .entry(nr.clone())
57 .and_modify(|c| {
58 if c.count == 1 {
59 *has_duplicate = true;
60 }
61 c.count += 1;
62 })
63 .or_insert(ReadCount { count: 1, has_been_mapped: false });
64 }
65
66 fn add_from_children(&self, nr: &NamedReference) -> bool {
67 if self.parent_state.is_some_and(|pc| pc.add_from_children(nr)) {
68 return true;
69 }
70 let mut use_counts = self.counts.borrow_mut();
71 let use_counts = &mut *use_counts;
72 if let Some(c) = use_counts.counts.get_mut(nr) {
73 if c.count == 1 {
74 use_counts.has_duplicate = true;
75 }
76 c.count += 1;
77 true
78 } else {
79 false
80 }
81 }
82
83 fn get_mapping(&self, nr: &NamedReference) -> Option<SmolStr> {
84 self.parent_state.and_then(|pr| pr.get_mapping(nr)).or_else(|| {
85 self.counts.borrow_mut().counts.get_mut(nr).filter(|c| c.count > 1).map(|c| {
86 c.has_been_mapped = true;
87 map_nr(nr)
88 })
89 })
90 }
91}
92
93fn map_nr(nr: &NamedReference) -> SmolStr {
94 format_smolstr!("tmp_{}_{}", nr.element().borrow().id, nr.name())
95}
96
97fn process_expression(expr: &mut Expression, old_state: &DedupPropState) {
98 let new_state = DedupPropState { parent_state: Some(old_state), ..DedupPropState::default() };
99 collect_unconditional_read_count(expr, &new_state);
100 process_conditional_expressions(expr, &new_state);
101 do_replacements(expr, &new_state);
102
103 if new_state.counts.borrow().has_duplicate {
104 let mut stores = BTreeMap::<SmolStr, NamedReference>::new();
105 for (nr, c) in &new_state.counts.borrow().counts {
106 if c.has_been_mapped {
107 stores.insert(map_nr(nr), nr.clone());
108 }
109 }
110 let mut exprs = stores
111 .into_iter()
112 .map(|(name, nr)| Expression::StoreLocalVariable {
113 name,
114 value: Box::new(Expression::PropertyReference(nr)),
115 })
116 .collect::<Vec<_>>();
117 exprs.push(std::mem::take(expr));
118 *expr = Expression::CodeBlock(exprs);
119 }
120}
121
122fn collect_unconditional_read_count(expr: &Expression, result: &DedupPropState) {
124 match expr {
125 Expression::PropertyReference(nr) => {
126 result.add(nr);
127 }
128 Expression::BinaryExpression { lhs, rhs: _, op: '|' | '&', .. } => {
131 lhs.visit(|sub| collect_unconditional_read_count(sub, result))
132 }
133 Expression::Condition { condition, .. } => {
134 condition.visit(|sub| collect_unconditional_read_count(sub, result))
135 }
136 _ => expr.visit(|sub| collect_unconditional_read_count(sub, result)),
137 }
138}
139
140fn process_conditional_expressions(expr: &mut Expression, state: &DedupPropState) {
141 match expr {
142 Expression::BinaryExpression { lhs, rhs, op: '|' | '&', .. } => {
143 lhs.visit_mut(|sub| process_conditional_expressions(sub, state));
144 process_expression(rhs, state);
145 }
146 Expression::Condition { condition, true_expr, false_expr, .. } => {
147 condition.visit_mut(|sub| process_conditional_expressions(sub, state));
148 process_expression(true_expr, state);
149 process_expression(false_expr, state);
150 }
151 _ => expr.visit_mut(|sub| process_conditional_expressions(sub, state)),
152 }
153}
154
155fn do_replacements(expr: &mut Expression, state: &DedupPropState) {
156 match expr {
157 Expression::PropertyReference(nr) => {
158 if let Some(name) = state.get_mapping(nr) {
159 let ty = expr.ty();
160 *expr = Expression::ReadLocalVariable { name, ty };
161 }
162 }
163 Expression::BinaryExpression { lhs, rhs: _, op: '|' | '&', .. } => {
164 lhs.visit_mut(|sub| do_replacements(sub, state));
165 }
166 Expression::Condition { condition, .. } => {
167 condition.visit_mut(|sub| do_replacements(sub, state));
168 }
169 _ => expr.visit_mut(|sub| do_replacements(sub, state)),
170 }
171}