slint/internal/compiler/llr/optim_passes/count_property_use.rs
Tobias Hunger 90e55bd29f compiler: Fix a couple of clippy issues
Nothing major, nothing controversial, just killing time
while waiting on CI.
2024-08-26 08:45:52 +02:00

191 lines
7 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
//! Passes that fills the Property::use_count
//!
//! This pass assume that use_count of all properties is zero
use crate::llr::{
Animation, BindingExpression, CompilationUnit, EvaluationContext, Expression, ParentCtx,
PropertyReference,
};
pub fn count_property_use(root: &CompilationUnit) {
// Visit the root properties that are used.
// 1. the public properties
for c in &root.public_components {
let root_ctx = EvaluationContext::new_sub_component(root, &c.item_tree.root, (), None);
for p in c.public_properties.iter().filter(|p| {
!matches!(
p.prop,
PropertyReference::Function { .. } | PropertyReference::GlobalFunction { .. }
)
}) {
visit_property(&p.prop, &root_ctx);
}
}
for g in root.globals.iter().filter(|g| g.exported) {
let ctx = EvaluationContext::new_global(root, g, ());
for p in g.public_properties.iter().filter(|p| {
!matches!(
p.prop,
PropertyReference::Function { .. } | PropertyReference::GlobalFunction { .. }
)
}) {
visit_property(&p.prop, &ctx);
}
}
root.for_each_sub_components(&mut |sc, ctx| {
// 2. the native items and bindings of used properties
for (pr, expr) in &sc.property_init {
match pr {
PropertyReference::Local { sub_component_path, property_index } => {
let mut sc = sc;
for i in sub_component_path {
sc = &sc.sub_components[*i].ty;
}
if sc.properties[*property_index].use_count.get() == 0 {
continue;
}
}
PropertyReference::InNativeItem { .. } => {}
_ => unreachable!(),
}
let c = expr.use_count.get();
expr.use_count.set(c + 1);
if c == 0 {
visit_binding_expression(expr, ctx)
}
}
// 3. the init code
for expr in &sc.init_code {
expr.borrow().visit_property_references(ctx, &mut visit_property);
}
// 4. the models
for (idx, r) in sc.repeated.iter().enumerate() {
r.model.borrow().visit_property_references(ctx, &mut visit_property);
if let Some(lv) = &r.listview {
visit_property(&lv.viewport_y, ctx);
visit_property(&lv.viewport_width, ctx);
visit_property(&lv.viewport_height, ctx);
visit_property(&lv.listview_width, ctx);
visit_property(&lv.listview_height, ctx);
let rep_ctx = EvaluationContext::new_sub_component(
root,
&r.sub_tree.root,
(),
Some(ParentCtx::new(ctx, Some(idx as u32))),
);
visit_property(&lv.prop_y, &rep_ctx);
visit_property(&lv.prop_width, &rep_ctx);
visit_property(&lv.prop_height, &rep_ctx);
}
for idx in r.data_prop.iter().chain(r.index_prop.iter()) {
// prevent optimizing model properties
let p = &r.sub_tree.root.properties[*idx];
p.use_count.set(2);
}
}
// 5. the layout info
sc.layout_info_h.borrow().visit_property_references(ctx, &mut visit_property);
sc.layout_info_v.borrow().visit_property_references(ctx, &mut visit_property);
// 6. accessibility props and geometries
for b in sc.accessible_prop.values() {
b.borrow().visit_property_references(ctx, &mut visit_property)
}
for i in sc.geometries.iter().filter_map(Option::as_ref) {
i.borrow().visit_property_references(ctx, &mut visit_property)
}
// 7. aliases (if they were not optimize, they are probably used)
for (a, b) in &sc.two_way_bindings {
visit_property(a, ctx);
visit_property(b, ctx);
}
// 8.functions (TODO: only visit used function)
for f in &sc.functions {
f.code.visit_property_references(ctx, &mut visit_property);
}
// 9. change callbacks
for (p, e) in &sc.change_callbacks {
visit_property(p, ctx);
e.borrow().visit_property_references(ctx, &mut visit_property);
}
// 10. popup x/y coordinates
for popup in &sc.popup_windows {
let popup_ctx = EvaluationContext::new_sub_component(
root,
&popup.item_tree.root,
(),
Some(ParentCtx::new(&ctx, None)),
);
popup.position.borrow().visit_property_references(&popup_ctx, &mut visit_property)
}
// 11. timer
for timer in &sc.timers {
timer.interval.borrow().visit_property_references(ctx, &mut visit_property);
timer.running.borrow().visit_property_references(ctx, &mut visit_property);
timer.triggered.borrow().visit_property_references(ctx, &mut visit_property);
}
});
// TODO: only visit used function
for g in root.globals.iter() {
let ctx = EvaluationContext::new_global(root, g, ());
for f in &g.functions {
f.code.visit_property_references(&ctx, &mut visit_property);
}
}
clean_unused_bindings(root);
}
fn visit_property(pr: &PropertyReference, ctx: &EvaluationContext) {
let p_info = ctx.property_info(pr);
if let Some(p) = &p_info.property_decl {
p.use_count.set(p.use_count.get() + 1);
}
if let Some((binding, map)) = &p_info.binding {
let c = binding.use_count.get();
binding.use_count.set(c + 1);
if c == 0 {
let ctx2 = map.map_context(ctx);
visit_binding_expression(binding, &ctx2);
}
}
}
fn visit_binding_expression(binding: &BindingExpression, ctx: &EvaluationContext) {
binding.expression.borrow().visit_property_references(ctx, &mut visit_property);
match &binding.animation {
Some(Animation::Static(e) | Animation::Transition(e)) => {
e.visit_property_references(ctx, &mut visit_property)
}
None => (),
}
}
/// Bindings which have a use_count of zero can be cleared so that we won't ever visit them later.
fn clean_unused_bindings(root: &CompilationUnit) {
root.for_each_sub_components(&mut |sc, _| {
for (_, e) in &sc.property_init {
if e.use_count.get() == 0 {
e.expression.replace(Expression::CodeBlock(vec![]));
}
}
});
for g in &root.globals {
for e in g.init_values.iter().flatten() {
if e.use_count.get() == 0 {
e.expression.replace(Expression::CodeBlock(vec![]));
}
}
}
}