Skip to content

Commit

Permalink
feat(es/minifier): Make minifier parallel (#2009)
Browse files Browse the repository at this point in the history
swc_ecma_minifier:
 - Introduce `bundle` mode, which can be used to parallelize processing of bundled files.
 - Add a function analyzer that checks if the function references something from the outer scope.
 - Split out parellsizable passes.
 - Split `optimzer` into pure / non-pure.
 - Run pure optimizations in parallel.
  • Loading branch information
kdy1 authored Aug 7, 2021
1 parent 8a39c1d commit 026c21e
Show file tree
Hide file tree
Showing 63 changed files with 4,595 additions and 3,517 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,11 @@ lto = true
[profile.bench]
codegen-units = 1
debug = true

# Without this, printing diff consumes more than a minute.

[profile.dev.package.pretty_assertions]
opt-level = 3

[profile.test.package.pretty_assertions]
opt-level = 3
3 changes: 2 additions & 1 deletion ecmascript/minifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
license = "Apache-2.0/MIT"
name = "swc_ecma_minifier"
repository = "https://github.com/swc-project/swc.git"
version = "0.18.1"
version = "0.18.2"

[features]
debug = []
Expand All @@ -18,6 +18,7 @@ indexmap = "1.7.0"
log = "0.4"
once_cell = "1.5.2"
pretty_assertions = {version = "0.6.1", optional = true}
rayon = "1.5.1"
regex = "1.5.3"
retain_mut = "0.1.2"
serde = {version = "1.0.118", features = ["derive"]}
Expand Down
3 changes: 3 additions & 0 deletions ecmascript/minifier/src/analyzer/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ impl UsageAnalyzer {

#[derive(Debug, Default, Clone, Copy)]
pub(super) struct Ctx {
/// See [crate::marks::Marks]
pub skip_standalone: bool,

pub var_decl_kind_of_pat: Option<VarDeclKind>,

pub in_var_decl_with_no_side_effect_for_member_access: bool,
Expand Down
57 changes: 44 additions & 13 deletions ecmascript/minifier/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ mod ctx;

/// TODO: Track assignments to variables via `arguments`.
/// TODO: Scope-local. (Including block)
pub(crate) fn analyze<N>(n: &N, marks: Marks) -> ProgramData
///
/// If `marks` is [None], markers are ignored.
pub(crate) fn analyze<N>(n: &N, marks: Option<Marks>) -> ProgramData
where
N: VisitWith<UsageAnalyzer>,
{
Expand Down Expand Up @@ -237,7 +239,7 @@ impl ProgramData {
#[derive(Debug)]
pub(crate) struct UsageAnalyzer {
data: ProgramData,
marks: Marks,
marks: Option<Marks>,
scope: ScopeData,
ctx: Ctx,
}
Expand Down Expand Up @@ -415,7 +417,11 @@ impl Visit for UsageAnalyzer {
}

fn visit_call_expr(&mut self, n: &CallExpr, _: &dyn Node) {
let inline_prevented = self.ctx.inline_prevented || n.span.has_mark(self.marks.noinline);
let inline_prevented = self.ctx.inline_prevented
|| self
.marks
.map(|marks| n.span.has_mark(marks.noinline))
.unwrap_or_default();

{
let ctx = Ctx {
Expand Down Expand Up @@ -588,18 +594,35 @@ impl Visit for UsageAnalyzer {
fn visit_function(&mut self, n: &Function, _: &dyn Node) {
n.decorators.visit_with(n, self);

self.with_child(n.span.ctxt, ScopeKind::Fn, |child| {
n.params.visit_with(n, child);
let is_standalone = self
.marks
.map(|marks| n.span.has_mark(marks.standalone))
.unwrap_or_default();

match &n.body {
Some(body) => {
// We use visit_children_with instead of visit_with to bypass block scope
// handler.
body.visit_children_with(child);
// We don't dig into standalone function, as it does not share any variable with
// outer scope.
if self.ctx.skip_standalone && is_standalone {
return;
}

let ctx = Ctx {
skip_standalone: self.ctx.skip_standalone || is_standalone,
..self.ctx
};

self.with_ctx(ctx)
.with_child(n.span.ctxt, ScopeKind::Fn, |child| {
n.params.visit_with(n, child);

match &n.body {
Some(body) => {
// We use visit_children_with instead of visit_with to bypass block scope
// handler.
body.visit_children_with(child);
}
None => {}
}
None => {}
}
})
})
}

fn visit_if_stmt(&mut self, n: &IfStmt, _: &dyn Node) {
Expand Down Expand Up @@ -665,6 +688,14 @@ impl Visit for UsageAnalyzer {
}
}

fn visit_module(&mut self, n: &Module, _: &dyn Node) {
let ctx = Ctx {
skip_standalone: true,
..self.ctx
};
n.visit_children_with(&mut *self.with_ctx(ctx))
}

fn visit_named_export(&mut self, n: &NamedExport, _: &dyn Node) {
if n.src.is_some() {
return;
Expand Down
Loading

0 comments on commit 026c21e

Please sign in to comment.