Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(es/minifier): Make analyzer not call collect_infects_from recursively #9924

Merged
merged 11 commits into from
Jan 28, 2025
Merged
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ninety-owls-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
swc_ecma_usage_analyzer: major
---

perf(es/minifier): Add cache for `collect_infects_from`
8 changes: 3 additions & 5 deletions crates/swc_ecma_minifier/src/compress/optimize/inline.rs
Original file line number Diff line number Diff line change
@@ -685,11 +685,9 @@ impl Optimizer<'_> {

for i in collect_infects_from(
&f.function,
AliasConfig {
marks: Some(self.marks),
ignore_nested: false,
need_all: true,
},
AliasConfig::default()
.marks(Some(self.marks))
.need_all(true),
) {
if let Some(usage) = self.data.vars.get_mut(&i.0) {
usage.ref_count += 1;
27 changes: 12 additions & 15 deletions crates/swc_ecma_minifier/src/compress/optimize/sequences.rs
Original file line number Diff line number Diff line change
@@ -1150,33 +1150,30 @@ impl Optimizer<'_> {
Mergable::Var(a) => a.init.as_ref().map(|init| {
collect_infects_from(
init,
AliasConfig {
marks: Some(self.marks),
ignore_nested: true,
need_all: true,
},
AliasConfig::default()
.marks(Some(self.marks))
.ignore_nested(true)
.need_all(true),
)
}),
Mergable::Expr(a) => match a {
Expr::Assign(a) if a.is_simple_assign() => Some(collect_infects_from(
&a.right,
AliasConfig {
marks: Some(self.marks),
ignore_nested: true,
need_all: true,
},
AliasConfig::default()
.marks(Some(self.marks))
.ignore_nested(true)
.need_all(true),
)),

_ => None,
},

Mergable::FnDecl(a) => Some(collect_infects_from(
&a.function,
AliasConfig {
marks: Some(self.marks),
ignore_nested: true,
need_all: true,
},
AliasConfig::default()
.marks(Some(self.marks))
.ignore_nested(true)
.need_all(true),
)),

Mergable::Drop => return false,

Large diffs are not rendered by default.

107 changes: 86 additions & 21 deletions crates/swc_ecma_usage_analyzer/src/alias/mod.rs
Original file line number Diff line number Diff line change
@@ -12,12 +12,41 @@ use crate::{marks::Marks, util::is_global_var_with_pure_property_access};
mod ctx;

#[derive(Default)]
#[non_exhaustive]
pub struct AliasConfig {
pub marks: Option<Marks>,
pub ignore_nested: bool,
/// TODO(kdy1): This field is used for sequential inliner.
/// It should be renamed to some correct name.
pub need_all: bool,

/// We can skip visiting children nodes in some cases.
///
/// Because we recurse in the usage analyzer, we don't need to recurse into
/// child node that the usage analyzer will invoke [`collect_infects_from`]
/// on.
pub ignore_named_child_scope: bool,
}
impl AliasConfig {
pub fn marks(mut self, arg: Option<Marks>) -> Self {
self.marks = arg;
self
}

pub fn ignore_nested(mut self, arg: bool) -> Self {
self.ignore_nested = arg;
self
}

pub fn ignore_named_child_scope(mut self, arg: bool) -> Self {
self.ignore_named_child_scope = arg;
self
}

pub fn need_all(mut self, arg: bool) -> Self {
self.need_all = arg;
self
}
}

pub trait InfectableNode {
@@ -99,8 +128,8 @@ pub struct InfectionCollector<'a> {
}

impl InfectionCollector<'_> {
fn add_id(&mut self, e: &Id) {
if self.exclude.contains(e) {
fn add_id(&mut self, e: Id) {
if self.exclude.contains(&e) {
return;
}

@@ -109,7 +138,7 @@ impl InfectionCollector<'_> {
}

self.accesses.insert((
e.clone(),
e,
if self.ctx.is_callee {
AccessKind::Call
} else {
@@ -122,6 +151,17 @@ impl InfectionCollector<'_> {
impl Visit for InfectionCollector<'_> {
noop_visit_type!();

fn visit_assign_expr(&mut self, n: &AssignExpr) {
if self.config.ignore_named_child_scope
&& n.op == op!("=")
&& n.left.as_simple().and_then(|l| l.leftmost()).is_some()
{
return;
}

n.visit_children_with(self);
}

fn visit_bin_expr(&mut self, e: &BinExpr) {
match e.op {
op!("in")
@@ -163,6 +203,14 @@ impl Visit for InfectionCollector<'_> {
}
}

fn visit_callee(&mut self, n: &Callee) {
let ctx = Ctx {
is_callee: true,
..self.ctx
};
n.visit_children_with(&mut *self.with_ctx(ctx));
}

fn visit_cond_expr(&mut self, e: &CondExpr) {
{
let ctx = Ctx {
@@ -183,15 +231,11 @@ impl Visit for InfectionCollector<'_> {
}
}

fn visit_ident(&mut self, n: &Ident) {
self.add_id(&n.to_id());
}

fn visit_expr(&mut self, e: &Expr) {
match e {
Expr::Ident(i) => {
if self.ctx.track_expr_ident {
self.add_id(&i.to_id());
self.add_id(i.to_id());
}
}

@@ -205,6 +249,25 @@ impl Visit for InfectionCollector<'_> {
}
}

fn visit_fn_decl(&mut self, n: &FnDecl) {
if self.config.ignore_named_child_scope {
return;
}

n.visit_children_with(self);
}

fn visit_fn_expr(&mut self, n: &FnExpr) {
if self.config.ignore_named_child_scope && n.ident.is_some() {
return;
}
n.visit_children_with(self);
}

fn visit_ident(&mut self, n: &Ident) {
self.add_id(n.to_id());
}

fn visit_member_expr(&mut self, n: &MemberExpr) {
{
let ctx = Ctx {
@@ -232,6 +295,15 @@ impl Visit for InfectionCollector<'_> {
}
}

fn visit_prop_name(&mut self, n: &PropName) {
if let PropName::Computed(c) = &n {
c.visit_with(&mut *self.with_ctx(Ctx {
is_callee: false,
..self.ctx
}));
}
}

fn visit_super_prop_expr(&mut self, n: &SuperPropExpr) {
if let SuperProp::Computed(c) = &n.prop {
c.visit_with(&mut *self.with_ctx(Ctx {
@@ -277,20 +349,13 @@ impl Visit for InfectionCollector<'_> {
e.arg.visit_with(&mut *self.with_ctx(ctx));
}

fn visit_prop_name(&mut self, n: &PropName) {
if let PropName::Computed(c) = &n {
c.visit_with(&mut *self.with_ctx(Ctx {
is_callee: false,
..self.ctx
}));
fn visit_var_declarator(&mut self, n: &VarDeclarator) {
if self.config.ignore_named_child_scope {
if let (Pat::Ident(..), Some(..)) = (&n.name, n.init.as_deref()) {
return;
}
}
}

fn visit_callee(&mut self, n: &Callee) {
let ctx = Ctx {
is_callee: true,
..self.ctx
};
n.visit_children_with(&mut *self.with_ctx(ctx));
n.visit_children_with(self);
}
}
4 changes: 4 additions & 0 deletions crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
@@ -306,6 +306,7 @@ where
&n.right,
AliasConfig {
marks: self.marks,
ignore_named_child_scope: true,
..Default::default()
},
) {
@@ -758,6 +759,7 @@ where
&n.function,
AliasConfig {
marks: self.marks,
ignore_named_child_scope: true,
..Default::default()
},
) {
@@ -788,6 +790,7 @@ where
&n.function,
AliasConfig {
marks: self.marks,
ignore_named_child_scope: true,
..Default::default()
},
) {
@@ -1285,6 +1288,7 @@ where
init,
AliasConfig {
marks: self.marks,
ignore_named_child_scope: true,
..Default::default()
},
) {