i_slint_compiler/llr/optim_passes/
inline_expressions.rs1use crate::expression_tree::{BuiltinFunction, ImageReference};
10use crate::llr::{CompilationUnit, EvaluationContext, Expression};
11
12const PROPERTY_ACCESS_COST: isize = 1000;
13const ALLOC_COST: isize = 700;
14const ARRAY_INDEX_COST: isize = 500;
15const INLINE_THRESHOLD: isize = ALLOC_COST * 2 - 10;
18const INLINE_SINGLE_THRESHOLD: isize = ALLOC_COST * 10;
20
21fn expression_cost(exp: &Expression, ctx: &EvaluationContext) -> isize {
23 let mut cost = match exp {
24 Expression::StringLiteral(_) => ALLOC_COST,
25 Expression::NumberLiteral(_) => 0,
26 Expression::BoolLiteral(_) => 0,
27 Expression::KeysLiteral(_) => 0,
28 Expression::PropertyReference(_) => PROPERTY_ACCESS_COST,
29 Expression::FunctionParameterReference { .. } => return isize::MAX,
30 Expression::StoreLocalVariable { .. } => 0,
31 Expression::ReadLocalVariable { .. } => 1,
32 Expression::StructFieldAccess { .. } => 1,
33 Expression::ArrayIndex { .. } => ARRAY_INDEX_COST,
34 Expression::Cast { .. } => 0,
35 Expression::CodeBlock(_) => 0,
36 Expression::BuiltinFunctionCall { function, .. } => builtin_function_cost(function),
37 Expression::CallBackCall { callback, .. } => callback_cost(callback, ctx),
38 Expression::FunctionCall { function, .. } => callback_cost(function, ctx),
39 Expression::ItemMemberFunctionCall { function } => callback_cost(function, ctx),
40 Expression::ExtraBuiltinFunctionCall { .. } => return isize::MAX,
41 Expression::PropertyAssignment { .. } => return isize::MAX,
42 Expression::ModelDataAssignment { .. } => return isize::MAX,
43 Expression::ArrayIndexAssignment { .. } => return isize::MAX,
44 Expression::SliceIndexAssignment { .. } => return isize::MAX,
45 Expression::BinaryExpression { .. } => 1,
46 Expression::UnaryOp { .. } => 1,
47 Expression::ImageReference {
51 resource_ref: ImageReference::EmbeddedTexture { .. }, ..
52 } => 1,
53 Expression::ImageReference { .. } => return isize::MAX,
54 Expression::Condition { condition, true_expr, false_expr } => {
55 return expression_cost(condition, ctx)
56 .saturating_add(
57 expression_cost(true_expr, ctx).max(expression_cost(false_expr, ctx)),
58 )
59 .saturating_add(10);
60 }
61 Expression::Array { .. } => return isize::MAX,
64 Expression::Struct { .. } => 1,
65 Expression::EasingCurve(_) => 1,
66 Expression::LinearGradient { .. } => ALLOC_COST,
67 Expression::RadialGradient { .. } => ALLOC_COST,
68 Expression::ConicGradient { .. } => ALLOC_COST,
69 Expression::EnumerationValue(_) => 0,
70 Expression::LayoutCacheAccess { .. } => PROPERTY_ACCESS_COST,
71 Expression::GridRepeaterCacheAccess { .. } => PROPERTY_ACCESS_COST,
72 Expression::WithLayoutItemInfo { .. } => return isize::MAX,
73 Expression::WithFlexboxLayoutItemInfo { .. } => return isize::MAX,
74 Expression::SolveFlexboxLayoutWithMeasure { .. } => return isize::MAX,
75 Expression::WithGridInputData { .. } => return isize::MAX,
76 Expression::MinMax { .. } => 10,
77 Expression::EmptyComponentFactory => 10,
78 Expression::EmptyDataTransfer => 10,
79 Expression::TranslationReference { .. } => PROPERTY_ACCESS_COST + 2 * ALLOC_COST,
80 };
81
82 exp.visit(|e| cost = cost.saturating_add(expression_cost(e, ctx)));
83
84 cost
85}
86
87fn callback_cost(_callback: &crate::llr::MemberReference, _ctx: &EvaluationContext) -> isize {
88 isize::MAX
90}
91
92fn builtin_function_cost(function: &BuiltinFunction) -> isize {
93 match function {
94 BuiltinFunction::GetWindowScaleFactor => PROPERTY_ACCESS_COST,
95 BuiltinFunction::GetWindowDefaultFontSize => PROPERTY_ACCESS_COST,
96 BuiltinFunction::AnimationTick => PROPERTY_ACCESS_COST,
97 BuiltinFunction::DecimalSeparator => PROPERTY_ACCESS_COST,
98 BuiltinFunction::Debug => isize::MAX,
99 BuiltinFunction::Mod => 10,
100 BuiltinFunction::Round => 10,
101 BuiltinFunction::Ceil => 10,
102 BuiltinFunction::Floor => 10,
103 BuiltinFunction::Abs => 10,
104 BuiltinFunction::Sqrt => 10,
105 BuiltinFunction::Cos => 10,
106 BuiltinFunction::Sin => 10,
107 BuiltinFunction::Tan => 10,
108 BuiltinFunction::ACos => 10,
109 BuiltinFunction::ASin => 10,
110 BuiltinFunction::ATan => 10,
111 BuiltinFunction::ATan2 => 10,
112 BuiltinFunction::Log => 10,
113 BuiltinFunction::Ln => 10,
114 BuiltinFunction::Pow => 10,
115 BuiltinFunction::Exp => 10,
116 BuiltinFunction::ToFixed => ALLOC_COST,
117 BuiltinFunction::ToPrecision => ALLOC_COST,
118 BuiltinFunction::ToStringUnlocalized => ALLOC_COST,
119 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => isize::MAX,
120 BuiltinFunction::ShowPopupWindow
121 | BuiltinFunction::ClosePopupWindow
122 | BuiltinFunction::ShowPopupMenu
123 | BuiltinFunction::ShowPopupMenuInternal => isize::MAX,
124 BuiltinFunction::SetSelectionOffsets => isize::MAX,
125 BuiltinFunction::ItemFontMetrics => PROPERTY_ACCESS_COST,
126 BuiltinFunction::StringToFloat => 50,
127 BuiltinFunction::StringIsFloat => 50,
128 BuiltinFunction::StringIsEmpty => 50,
129 BuiltinFunction::StringCharacterCount => 50,
130 BuiltinFunction::StringToLowercase | BuiltinFunction::StringToUppercase => ALLOC_COST,
131 BuiltinFunction::KeysToString => ALLOC_COST,
132 BuiltinFunction::ColorRgbaStruct => 50,
133 BuiltinFunction::ColorHsvaStruct => 50,
134 BuiltinFunction::ColorOklchStruct => 50,
135 BuiltinFunction::ColorBrighter => 50,
136 BuiltinFunction::ColorDarker => 50,
137 BuiltinFunction::ColorTransparentize => 50,
138 BuiltinFunction::ColorMix => 50,
139 BuiltinFunction::ColorWithAlpha => 50,
140 BuiltinFunction::ImageSize => 50,
141 BuiltinFunction::ArrayLength => 50,
142 BuiltinFunction::Rgb => 50,
143 BuiltinFunction::Hsv => 50,
144 BuiltinFunction::Oklch => 50,
145 BuiltinFunction::ImplicitLayoutInfo(_) => isize::MAX,
146 BuiltinFunction::ItemAbsolutePosition => isize::MAX,
147 BuiltinFunction::RegisterCustomFontByPath => isize::MAX,
148 BuiltinFunction::RegisterCustomFontByMemory => isize::MAX,
149 BuiltinFunction::RegisterBitmapFont => isize::MAX,
150 BuiltinFunction::ColorScheme => PROPERTY_ACCESS_COST,
151 BuiltinFunction::AccentColor => PROPERTY_ACCESS_COST,
152 BuiltinFunction::SupportsNativeMenuBar => 10,
153 BuiltinFunction::SetupMenuBar => isize::MAX,
154 BuiltinFunction::SetupSystemTrayIcon => isize::MAX,
155 BuiltinFunction::MonthDayCount => isize::MAX,
156 BuiltinFunction::MonthOffset => isize::MAX,
157 BuiltinFunction::FormatDate => isize::MAX,
158 BuiltinFunction::DateNow => isize::MAX,
159 BuiltinFunction::ValidDate => isize::MAX,
160 BuiltinFunction::ParseDate => isize::MAX,
161 BuiltinFunction::SetTextInputFocused => PROPERTY_ACCESS_COST,
162 BuiltinFunction::TextInputFocused => PROPERTY_ACCESS_COST,
163 BuiltinFunction::Translate => 2 * ALLOC_COST + PROPERTY_ACCESS_COST,
164 BuiltinFunction::Use24HourFormat => 2 * ALLOC_COST + PROPERTY_ACCESS_COST,
165 BuiltinFunction::UpdateTimers => 10,
166 BuiltinFunction::DetectOperatingSystem => 10,
167 BuiltinFunction::StartTimer => 10,
168 BuiltinFunction::StopTimer => 10,
169 BuiltinFunction::RestartTimer => 10,
170 BuiltinFunction::ParseMarkdown => isize::MAX,
171 BuiltinFunction::StringToStyledText => ALLOC_COST,
172 BuiltinFunction::ColorToStyledText => ALLOC_COST,
173 BuiltinFunction::OpenUrl => isize::MAX,
174 BuiltinFunction::MacosBringAllWindowsToFront => isize::MAX,
175 }
176}
177
178pub fn inline_simple_expressions(root: &CompilationUnit) {
179 root.for_each_expression(&mut |e, ctx| {
180 inline_simple_expressions_in_expression(&mut e.borrow_mut(), ctx)
181 })
182}
183
184fn inline_simple_expressions_in_expression(expr: &mut Expression, ctx: &EvaluationContext) {
185 if let Expression::PropertyReference(prop) = expr {
186 let prop_info = ctx.property_info(prop);
187 if prop_info.analysis.as_ref().is_some_and(|a| !a.is_set && !a.is_set_externally) {
188 if let Some((binding, map)) = prop_info.binding {
189 if binding.animation.is_none()
190 && !binding.is_state_info
192 {
193 let mapped_ctx = map.map_context(ctx);
194 let cost = expression_cost(&binding.expression.borrow(), &mapped_ctx);
195 let use_count = binding.use_count.get();
196 debug_assert!(
197 use_count > 0,
198 "We use a property and its count is zero: {}",
199 crate::llr::pretty_print::DisplayPropertyRef(prop, ctx)
200 );
201 if cost <= INLINE_THRESHOLD
202 || (use_count == 1 && cost <= INLINE_SINGLE_THRESHOLD)
203 {
204 *expr = binding.expression.borrow().clone();
206 map.map_expression(expr);
207 binding.use_count.set(use_count - 1);
209 if let Some(use_count) = prop_info.use_count {
210 use_count.set(use_count.get() - 1);
211 }
212 adjust_use_count(expr, ctx, 1);
213 if use_count == 1 {
214 adjust_use_count(&binding.expression.borrow(), &mapped_ctx, -1);
215 binding.expression.replace(Expression::CodeBlock(Vec::new()));
216 }
217 }
218 }
219 } else if let Some(use_count) = prop_info.use_count
220 && let Some(e) = Expression::default_value_for_type(&prop_info.ty)
221 {
222 use_count.set(use_count.get() - 1);
223 *expr = e;
224 }
225 }
226 };
227
228 expr.visit_mut(|e| inline_simple_expressions_in_expression(e, ctx));
229}
230
231fn adjust_use_count(expr: &Expression, ctx: &EvaluationContext, adjust: isize) {
232 expr.visit_property_references(ctx, &mut |p, ctx| {
233 let prop_info = ctx.property_info(p);
234 if let Some(use_count) = prop_info.use_count {
235 use_count.set(use_count.get().checked_add_signed(adjust).unwrap());
236 }
237 if let Some((binding, _)) = prop_info.binding {
238 let use_count = binding.use_count.get().checked_add_signed(adjust).unwrap();
239 binding.use_count.set(use_count);
240 }
241 });
242}