Skip to content

Commit 37616c3

Browse files
authoredJan 28, 2025··
perf(es/minifier): Make analyzer not call collect_infects_from recursively (#9924)
**Description:** We don't need to make time complexity non-linear by recursively visiting the effects of infection. Instead, suppose we stop the infection analyzer when the usage analyzer invokes the infection analyzer. In that case, we will have practically identical analysis results because the points are named using an id, and it points to all the infections that occurred by referencing the id. **Related issue:** - Closes #9927
1 parent 9109a53 commit 37616c3

File tree

5 files changed

+111
-41
lines changed

5 files changed

+111
-41
lines changed
 

‎.changeset/lovely-snakes-wink.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
¡---
2+
swc_ecma_usage_analyzer: major
3+
---
4+
5+
perf(es/minifier): Make analyzer not call `collect_infects_from` recursively

‎crates/swc_ecma_minifier/src/compress/optimize/inline.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -685,11 +685,9 @@ impl Optimizer<'_> {
685685

686686
for i in collect_infects_from(
687687
&f.function,
688-
AliasConfig {
689-
marks: Some(self.marks),
690-
ignore_nested: false,
691-
need_all: true,
692-
},
688+
AliasConfig::default()
689+
.marks(Some(self.marks))
690+
.need_all(true),
693691
) {
694692
if let Some(usage) = self.data.vars.get_mut(&i.0) {
695693
usage.ref_count += 1;

‎crates/swc_ecma_minifier/src/compress/optimize/sequences.rs

+12-15
Original file line numberDiff line numberDiff line change
@@ -1150,33 +1150,30 @@ impl Optimizer<'_> {
11501150
Mergable::Var(a) => a.init.as_ref().map(|init| {
11511151
collect_infects_from(
11521152
init,
1153-
AliasConfig {
1154-
marks: Some(self.marks),
1155-
ignore_nested: true,
1156-
need_all: true,
1157-
},
1153+
AliasConfig::default()
1154+
.marks(Some(self.marks))
1155+
.ignore_nested(true)
1156+
.need_all(true),
11581157
)
11591158
}),
11601159
Mergable::Expr(a) => match a {
11611160
Expr::Assign(a) if a.is_simple_assign() => Some(collect_infects_from(
11621161
&a.right,
1163-
AliasConfig {
1164-
marks: Some(self.marks),
1165-
ignore_nested: true,
1166-
need_all: true,
1167-
},
1162+
AliasConfig::default()
1163+
.marks(Some(self.marks))
1164+
.ignore_nested(true)
1165+
.need_all(true),
11681166
)),
11691167

11701168
_ => None,
11711169
},
11721170

11731171
Mergable::FnDecl(a) => Some(collect_infects_from(
11741172
&a.function,
1175-
AliasConfig {
1176-
marks: Some(self.marks),
1177-
ignore_nested: true,
1178-
need_all: true,
1179-
},
1173+
AliasConfig::default()
1174+
.marks(Some(self.marks))
1175+
.ignore_nested(true)
1176+
.need_all(true),
11801177
)),
11811178

11821179
Mergable::Drop => return false,

‎crates/swc_ecma_usage_analyzer/src/alias/mod.rs

+87-21
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,41 @@ use crate::{marks::Marks, util::is_global_var_with_pure_property_access};
1212
mod ctx;
1313

1414
#[derive(Default)]
15+
#[non_exhaustive]
1516
pub struct AliasConfig {
1617
pub marks: Option<Marks>,
1718
pub ignore_nested: bool,
1819
/// TODO(kdy1): This field is used for sequential inliner.
1920
/// It should be renamed to some correct name.
2021
pub need_all: bool,
22+
23+
/// We can skip visiting children nodes in some cases.
24+
///
25+
/// Because we recurse in the usage analyzer, we don't need to recurse into
26+
/// child node that the usage analyzer will invoke [`collect_infects_from`]
27+
/// on.
28+
pub ignore_named_child_scope: bool,
29+
}
30+
impl AliasConfig {
31+
pub fn marks(mut self, arg: Option<Marks>) -> Self {
32+
self.marks = arg;
33+
self
34+
}
35+
36+
pub fn ignore_nested(mut self, arg: bool) -> Self {
37+
self.ignore_nested = arg;
38+
self
39+
}
40+
41+
pub fn ignore_named_child_scope(mut self, arg: bool) -> Self {
42+
self.ignore_named_child_scope = arg;
43+
self
44+
}
45+
46+
pub fn need_all(mut self, arg: bool) -> Self {
47+
self.need_all = arg;
48+
self
49+
}
2150
}
2251

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

101130
impl InfectionCollector<'_> {
102-
fn add_id(&mut self, e: &Id) {
103-
if self.exclude.contains(e) {
131+
fn add_id(&mut self, e: Id) {
132+
if self.exclude.contains(&e) {
104133
return;
105134
}
106135

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

111140
self.accesses.insert((
112-
e.clone(),
141+
e,
113142
if self.ctx.is_callee {
114143
AccessKind::Call
115144
} else {
@@ -122,6 +151,18 @@ impl InfectionCollector<'_> {
122151
impl Visit for InfectionCollector<'_> {
123152
noop_visit_type!();
124153

154+
fn visit_assign_expr(&mut self, n: &AssignExpr) {
155+
if self.config.ignore_named_child_scope
156+
&& n.op == op!("=")
157+
&& n.left.as_simple().and_then(|l| l.leftmost()).is_some()
158+
{
159+
n.left.visit_with(self);
160+
return;
161+
}
162+
163+
n.visit_children_with(self);
164+
}
165+
125166
fn visit_bin_expr(&mut self, e: &BinExpr) {
126167
match e.op {
127168
op!("in")
@@ -163,6 +204,14 @@ impl Visit for InfectionCollector<'_> {
163204
}
164205
}
165206

207+
fn visit_callee(&mut self, n: &Callee) {
208+
let ctx = Ctx {
209+
is_callee: true,
210+
..self.ctx
211+
};
212+
n.visit_children_with(&mut *self.with_ctx(ctx));
213+
}
214+
166215
fn visit_cond_expr(&mut self, e: &CondExpr) {
167216
{
168217
let ctx = Ctx {
@@ -183,15 +232,11 @@ impl Visit for InfectionCollector<'_> {
183232
}
184233
}
185234

186-
fn visit_ident(&mut self, n: &Ident) {
187-
self.add_id(&n.to_id());
188-
}
189-
190235
fn visit_expr(&mut self, e: &Expr) {
191236
match e {
192237
Expr::Ident(i) => {
193238
if self.ctx.track_expr_ident {
194-
self.add_id(&i.to_id());
239+
self.add_id(i.to_id());
195240
}
196241
}
197242

@@ -205,6 +250,25 @@ impl Visit for InfectionCollector<'_> {
205250
}
206251
}
207252

253+
fn visit_fn_decl(&mut self, n: &FnDecl) {
254+
if self.config.ignore_named_child_scope {
255+
return;
256+
}
257+
258+
n.visit_children_with(self);
259+
}
260+
261+
fn visit_fn_expr(&mut self, n: &FnExpr) {
262+
if self.config.ignore_named_child_scope && n.ident.is_some() {
263+
return;
264+
}
265+
n.visit_children_with(self);
266+
}
267+
268+
fn visit_ident(&mut self, n: &Ident) {
269+
self.add_id(n.to_id());
270+
}
271+
208272
fn visit_member_expr(&mut self, n: &MemberExpr) {
209273
{
210274
let ctx = Ctx {
@@ -232,6 +296,15 @@ impl Visit for InfectionCollector<'_> {
232296
}
233297
}
234298

299+
fn visit_prop_name(&mut self, n: &PropName) {
300+
if let PropName::Computed(c) = &n {
301+
c.visit_with(&mut *self.with_ctx(Ctx {
302+
is_callee: false,
303+
..self.ctx
304+
}));
305+
}
306+
}
307+
235308
fn visit_super_prop_expr(&mut self, n: &SuperPropExpr) {
236309
if let SuperProp::Computed(c) = &n.prop {
237310
c.visit_with(&mut *self.with_ctx(Ctx {
@@ -277,20 +350,13 @@ impl Visit for InfectionCollector<'_> {
277350
e.arg.visit_with(&mut *self.with_ctx(ctx));
278351
}
279352

280-
fn visit_prop_name(&mut self, n: &PropName) {
281-
if let PropName::Computed(c) = &n {
282-
c.visit_with(&mut *self.with_ctx(Ctx {
283-
is_callee: false,
284-
..self.ctx
285-
}));
353+
fn visit_var_declarator(&mut self, n: &VarDeclarator) {
354+
if self.config.ignore_named_child_scope {
355+
if let (Pat::Ident(..), Some(..)) = (&n.name, n.init.as_deref()) {
356+
return;
357+
}
286358
}
287-
}
288359

289-
fn visit_callee(&mut self, n: &Callee) {
290-
let ctx = Ctx {
291-
is_callee: true,
292-
..self.ctx
293-
};
294-
n.visit_children_with(&mut *self.with_ctx(ctx));
360+
n.visit_children_with(self);
295361
}
296362
}

‎crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ where
306306
&n.right,
307307
AliasConfig {
308308
marks: self.marks,
309+
ignore_named_child_scope: true,
309310
..Default::default()
310311
},
311312
) {
@@ -758,6 +759,7 @@ where
758759
&n.function,
759760
AliasConfig {
760761
marks: self.marks,
762+
ignore_named_child_scope: true,
761763
..Default::default()
762764
},
763765
) {
@@ -788,6 +790,7 @@ where
788790
&n.function,
789791
AliasConfig {
790792
marks: self.marks,
793+
ignore_named_child_scope: true,
791794
..Default::default()
792795
},
793796
) {
@@ -1285,6 +1288,7 @@ where
12851288
init,
12861289
AliasConfig {
12871290
marks: self.marks,
1291+
ignore_named_child_scope: true,
12881292
..Default::default()
12891293
},
12901294
) {

0 commit comments

Comments
 (0)
Please sign in to comment.