Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions .changeset/batch-substitutions-if-dense.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: patch
swc_ecma_minifier: patch
---

perf(es/minifier): Apply pending substitutions in batch for if-dense statement lists
11 changes: 4 additions & 7 deletions crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,10 @@ impl Optimizer<'_> {
return;
}

// we must inline first to avoid https://github.com/swc-project/swc/issues/11517
stmts
.iter_mut()
.filter_map(|s| s.as_stmt_mut().and_then(|s| s.as_mut_if_stmt()))
.for_each(|s| {
self.changed |= self.vars.inline_with_multi_replacer(s);
});
// NOTE: The caller is responsible for applying pending substitutions
// to the statement list (see `handle_stmt_likes`, which does it in a
// single batch walk for `if`-dense lists) before merging. This is
// required to avoid https://github.com/swc-project/swc/issues/11517

let has_work =
stmts
Expand Down
29 changes: 29 additions & 0 deletions crates/swc_ecma_minifier/src/compress/optimize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ impl Optimizer<'_> {
where
T: StmtLike + ModuleItemLike + ModuleItemExt + VisitMutWith<Self> + VisitWith<AssertValid>,
Vec<T>: VisitMutWith<Self> + VisitWith<UsageAnalyzer<ProgramData>> + VisitWith<AssertValid>,
Vec<T>:
for<'aa> VisitMutWith<NormalMultiReplacer<'aa>> + for<'aa> VisitMutWith<Finalizer<'aa>>,
{
let mut use_asm = false;
let prepend_stmts = self.prepend_stmts.take();
Expand Down Expand Up @@ -546,6 +548,33 @@ impl Optimizer<'_> {
stmts.visit_with(&mut AssertValid);
}

// Apply pending substitutions (required before `merge_similar_ifs` to
// avoid https://github.com/swc-project/swc/issues/11517). Strategy
// depends on `if` density: for `if`-dense lists a single batch walk
// beats two walks per `if`; for `if`-sparse lists the per-`if` walks
// stay cheaper than walking the whole list twice. Both are
// semantically equivalent: the substitution maps do not change during
// application except for consumption of applied entries, which happens
// in the same statement order either way.
let if_count = stmts
.iter()
.filter(|s| {
s.as_stmt()
.map(|s| matches!(s, Stmt::If(_)))
.unwrap_or(false)
})
.count();
if if_count >= 16 {
self.changed |= self.vars.inline_with_multi_replacer(stmts);
} else {
stmts
.iter_mut()
.filter_map(|s| s.as_stmt_mut().and_then(|s| s.as_mut_if_stmt()))
.for_each(|s| {
self.changed |= self.vars.inline_with_multi_replacer(s);
});
}

self.merge_similar_ifs(stmts);

#[cfg(debug_assertions)]
Expand Down
Loading