perf(es/minifier): Optimize MultiReplacer (#5977)

**Description:**

This PR splits the `MultiReplacer` into two variants. The first one is the cloning version, and this is parallel. The other one is the non-cloning version, which is not parallel.
This commit is contained in:
Donny/강동윤 2022-09-28 18:19:30 +09:00 committed by GitHub
parent c23c07fc50
commit e37bb55101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 107 deletions

View File

@ -9,10 +9,7 @@ use swc_ecma_utils::{
}; };
use swc_ecma_visit::VisitMutWith; use swc_ecma_visit::VisitMutWith;
use super::{ use super::{util::NormalMultiReplacer, Optimizer};
util::{MultiReplacer, MultiReplacerMode},
Optimizer,
};
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
use crate::debug::dump; use crate::debug::dump;
use crate::{ use crate::{
@ -323,16 +320,13 @@ where
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))] #[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn inline_vars_in_node<N>(&mut self, n: &mut N, mut vars: FxHashMap<Id, Box<Expr>>) pub(super) fn inline_vars_in_node<N>(&mut self, n: &mut N, mut vars: FxHashMap<Id, Box<Expr>>)
where where
N: for<'aa> VisitMutWith<MultiReplacer<'aa>>, N: for<'aa> VisitMutWith<NormalMultiReplacer<'aa>>,
{ {
trace_op!("inline: inline_vars_in_node"); trace_op!("inline: inline_vars_in_node");
n.visit_mut_with(&mut MultiReplacer::new( let mut v = NormalMultiReplacer::new(&mut vars);
&mut vars, n.visit_mut_with(&mut v);
false, self.changed |= v.changed;
MultiReplacerMode::Normal,
&mut self.changed,
));
} }
/// Fully inlines iife. /// Fully inlines iife.

View File

@ -20,7 +20,7 @@ use Value::Known;
use self::{ use self::{
unused::PropertyAccessOpts, unused::PropertyAccessOpts,
util::{extract_class_side_effect, MultiReplacer, MultiReplacerMode}, util::{extract_class_side_effect, CloningMultiReplacer, NormalMultiReplacer},
}; };
use super::util::{drop_invalid_stmts, is_fine_for_if_cons}; use super::util::{drop_invalid_stmts, is_fine_for_if_cons};
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
@ -262,34 +262,24 @@ impl Vars {
/// Returns true if something is changed. /// Returns true if something is changed.
fn inline_with_multi_replacer<N>(&mut self, n: &mut N) -> bool fn inline_with_multi_replacer<N>(&mut self, n: &mut N) -> bool
where where
N: for<'aa> VisitMutWith<MultiReplacer<'aa>>, N: for<'aa> VisitMutWith<NormalMultiReplacer<'aa>>,
N: for<'aa> VisitMutWith<CloningMultiReplacer<'aa>>,
{ {
let mut changed = false; let mut changed = false;
if !self.simple_functions.is_empty() { if !self.simple_functions.is_empty() || !self.lits_for_cmp.is_empty() {
n.visit_mut_with(&mut MultiReplacer::new( let mut v = CloningMultiReplacer {
&mut self.simple_functions, lits_for_cmp: &self.lits_for_cmp,
true, simple_functions: &self.simple_functions,
MultiReplacerMode::OnlyCallee, changed: false,
&mut changed, };
)); n.visit_mut_with(&mut v);
} changed |= v.changed;
if !self.lits_for_cmp.is_empty() {
n.visit_mut_with(&mut MultiReplacer::new(
&mut self.lits_for_cmp,
true,
MultiReplacerMode::OnlyComparisonWithLit,
&mut changed,
));
} }
if !self.vars_for_inlining.is_empty() { if !self.vars_for_inlining.is_empty() {
n.visit_mut_with(&mut MultiReplacer::new( let mut v = NormalMultiReplacer::new(&mut self.vars_for_inlining);
&mut self.vars_for_inlining, n.visit_mut_with(&mut v);
false, changed |= v.changed;
MultiReplacerMode::Normal,
&mut changed,
));
} }
changed changed

View File

@ -4,12 +4,13 @@ use rustc_hash::FxHashMap;
use swc_atoms::js_word; use swc_atoms::js_word;
use swc_common::{Span, SyntaxContext, DUMMY_SP}; use swc_common::{Span, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*; use swc_ecma_ast::*;
use swc_ecma_transforms_base::perf::{Parallel, ParallelExt};
use swc_ecma_utils::{ExprCtx, ExprExt}; use swc_ecma_utils::{ExprCtx, ExprExt};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use tracing::debug; use tracing::debug;
use super::{Ctx, Optimizer}; use super::{Ctx, Optimizer};
use crate::mode::Mode; use crate::{mode::Mode, HEAVY_TASK_PARALLELS};
impl<'b, M> Optimizer<'b, M> impl<'b, M> Optimizer<'b, M>
where where
@ -156,43 +157,28 @@ impl VisitMut for Remapper {
} }
} }
pub(crate) struct MultiReplacer<'a> { #[derive(Clone, Copy)]
vars: &'a mut FxHashMap<Id, Box<Expr>>, pub(crate) struct CloningMultiReplacer<'a> {
clone: bool, pub simple_functions: &'a FxHashMap<Id, Box<Expr>>,
mode: MultiReplacerMode, pub lits_for_cmp: &'a FxHashMap<Id, Box<Expr>>,
worked: &'a mut bool, pub changed: bool,
} }
#[repr(u8)] impl Parallel for CloningMultiReplacer<'_> {
#[derive(Debug, Clone, Copy)] fn create(&self) -> Self {
*self
pub enum MultiReplacerMode {
Normal,
OnlyCallee,
OnlyComparisonWithLit,
}
impl<'a> MultiReplacer<'a> {
/// `worked` will be changed to `true` if any replacement is done
pub fn new(
vars: &'a mut FxHashMap<Id, Box<Expr>>,
clone: bool,
mode: MultiReplacerMode,
worked: &'a mut bool,
) -> Self {
MultiReplacer {
vars,
clone,
mode,
worked,
}
} }
fn var(&mut self, i: &Id) -> Option<Box<Expr>> { fn merge(&mut self, other: Self) {
let mut e = if self.clone { self.changed |= other.changed;
self.vars.get(i).cloned()? }
} else { }
self.vars.remove(i)?
impl<'a> CloningMultiReplacer<'a> {
fn var(&mut self, i: &Id, mode: MultiReplacerMode) -> Option<Box<Expr>> {
let mut e = match mode {
MultiReplacerMode::OnlyCallee => self.simple_functions.get(i).cloned()?,
MultiReplacerMode::OnlyComparisonWithLit => self.lits_for_cmp.get(i).cloned()?,
}; };
e.visit_mut_children_with(self); e.visit_mut_children_with(self);
@ -209,11 +195,11 @@ impl<'a> MultiReplacer<'a> {
} }
} }
fn check(&mut self, e: &mut Expr) { fn check(&mut self, e: &mut Expr, mode: MultiReplacerMode) {
if let Expr::Ident(i) = e { if let Expr::Ident(i) = e {
if let Some(new) = self.var(&i.to_id()) { if let Some(new) = self.var(&i.to_id(), mode) {
debug!("multi-replacer: Replaced `{}`", i); debug!("multi-replacer: Replaced `{}`", i);
*self.worked = true; self.changed = true;
*e = *new; *e = *new;
} }
@ -221,37 +207,111 @@ impl<'a> MultiReplacer<'a> {
} }
} }
impl VisitMut for MultiReplacer<'_> { #[derive(Debug, Clone, Copy)]
enum MultiReplacerMode {
OnlyCallee,
OnlyComparisonWithLit,
}
impl VisitMut for CloningMultiReplacer<'_> {
noop_visit_mut_type!(); noop_visit_mut_type!();
fn visit_mut_callee(&mut self, e: &mut Callee) { fn visit_mut_callee(&mut self, e: &mut Callee) {
e.visit_mut_children_with(self); e.visit_mut_children_with(self);
if matches!(self.mode, MultiReplacerMode::OnlyCallee) { if let Callee::Expr(e) = e {
if let Callee::Expr(e) = e { self.check(e, MultiReplacerMode::OnlyCallee);
self.check(e);
}
} }
} }
fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) { fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) {
e.visit_mut_children_with(self); e.visit_mut_children_with(self);
if let MultiReplacerMode::OnlyComparisonWithLit = self.mode { match e.op {
match e.op { op!("===") | op!("!==") | op!("==") | op!("!=") => {
op!("===") | op!("!==") | op!("==") | op!("!=") => { //
// if e.left.is_lit() {
if e.left.is_lit() { self.check(&mut e.right, MultiReplacerMode::OnlyComparisonWithLit);
self.check(&mut e.right); } else if e.right.is_lit() {
} else if e.right.is_lit() { self.check(&mut e.left, MultiReplacerMode::OnlyComparisonWithLit);
self.check(&mut e.left);
}
} }
_ => {}
} }
_ => {}
} }
} }
fn visit_mut_prop_or_spreads(&mut self, n: &mut Vec<PropOrSpread>) {
self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
n.visit_mut_with(v);
});
}
fn visit_mut_expr_or_spreads(&mut self, n: &mut Vec<ExprOrSpread>) {
self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
n.visit_mut_with(v);
});
}
fn visit_mut_opt_vec_expr_or_spreads(&mut self, n: &mut Vec<Option<ExprOrSpread>>) {
self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
n.visit_mut_with(v);
});
}
fn visit_mut_exprs(&mut self, n: &mut Vec<Box<Expr>>) {
self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
n.visit_mut_with(v);
});
}
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
n.visit_mut_with(v);
});
}
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
n.visit_mut_with(v);
});
}
}
pub(crate) struct NormalMultiReplacer<'a> {
pub vars: &'a mut FxHashMap<Id, Box<Expr>>,
pub changed: bool,
}
impl<'a> NormalMultiReplacer<'a> {
/// `worked` will be changed to `true` if any replacement is done
pub fn new(vars: &'a mut FxHashMap<Id, Box<Expr>>) -> Self {
NormalMultiReplacer {
vars,
changed: false,
}
}
fn var(&mut self, i: &Id) -> Option<Box<Expr>> {
let mut e = self.vars.remove(i)?;
e.visit_mut_children_with(self);
match &*e {
Expr::Ident(Ident {
sym: js_word!("eval"),
..
}) => Some(Box::new(Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: vec![0.into(), e],
}))),
_ => Some(e),
}
}
}
impl VisitMut for NormalMultiReplacer<'_> {
noop_visit_mut_type!();
fn visit_mut_expr(&mut self, e: &mut Expr) { fn visit_mut_expr(&mut self, e: &mut Expr) {
if self.vars.is_empty() { if self.vars.is_empty() {
return; return;
@ -262,14 +322,12 @@ impl VisitMut for MultiReplacer<'_> {
return; return;
} }
if matches!(self.mode, MultiReplacerMode::Normal) { if let Expr::Ident(i) = e {
if let Expr::Ident(i) = e { if let Some(new) = self.var(&i.to_id()) {
if let Some(new) = self.var(&i.to_id()) { debug!("multi-replacer: Replaced `{}`", i);
debug!("multi-replacer: Replaced `{}`", i); self.changed = true;
*self.worked = true;
*e = *new; *e = *new;
}
} }
} }
} }
@ -290,20 +348,18 @@ impl VisitMut for MultiReplacer<'_> {
fn visit_mut_prop(&mut self, p: &mut Prop) { fn visit_mut_prop(&mut self, p: &mut Prop) {
p.visit_mut_children_with(self); p.visit_mut_children_with(self);
if matches!(self.mode, MultiReplacerMode::Normal) { if let Prop::Shorthand(i) = p {
if let Prop::Shorthand(i) = p { if let Some(value) = self.var(&i.to_id()) {
if let Some(value) = self.var(&i.to_id()) { debug!("multi-replacer: Replaced `{}` as shorthand", i);
debug!("multi-replacer: Replaced `{}` as shorthand", i); self.changed = true;
*self.worked = true;
*p = Prop::KeyValue(KeyValueProp { *p = Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new( key: PropName::Ident(Ident::new(
i.sym.clone(), i.sym.clone(),
i.span.with_ctxt(Default::default()), i.span.with_ctxt(Default::default()),
)), )),
value, value,
}); });
}
} }
} }
} }