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 { fn merge(&mut self, other: Self) {
Normal, self.changed |= other.changed;
OnlyCallee, }
OnlyComparisonWithLit,
} }
impl<'a> MultiReplacer<'a> { impl<'a> CloningMultiReplacer<'a> {
/// `worked` will be changed to `true` if any replacement is done fn var(&mut self, i: &Id, mode: MultiReplacerMode) -> Option<Box<Expr>> {
pub fn new( let mut e = match mode {
vars: &'a mut FxHashMap<Id, Box<Expr>>, MultiReplacerMode::OnlyCallee => self.simple_functions.get(i).cloned()?,
clone: bool, MultiReplacerMode::OnlyComparisonWithLit => self.lits_for_cmp.get(i).cloned()?,
mode: MultiReplacerMode,
worked: &'a mut bool,
) -> Self {
MultiReplacer {
vars,
clone,
mode,
worked,
}
}
fn var(&mut self, i: &Id) -> Option<Box<Expr>> {
let mut e = if self.clone {
self.vars.get(i).cloned()?
} else {
self.vars.remove(i)?
}; };
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); self.check(e, MultiReplacerMode::OnlyCallee);
}
} }
} }
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); self.check(&mut e.right, MultiReplacerMode::OnlyComparisonWithLit);
} else if e.right.is_lit() { } else if e.right.is_lit() {
self.check(&mut e.left); self.check(&mut e.left, MultiReplacerMode::OnlyComparisonWithLit);
} }
} }
_ => {} _ => {}
} }
} }
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,17 +322,15 @@ 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.worked = true; self.changed = true;
*e = *new; *e = *new;
} }
} }
} }
}
fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) { fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
if self.vars.is_empty() { if self.vars.is_empty() {
@ -290,11 +348,10 @@ 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.worked = true; self.changed = true;
*p = Prop::KeyValue(KeyValueProp { *p = Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new( key: PropName::Ident(Ident::new(
@ -306,7 +363,6 @@ impl VisitMut for MultiReplacer<'_> {
} }
} }
} }
}
} }
pub(crate) fn replace_id_with_expr<N>(node: &mut N, from: Id, to: Box<Expr>) -> Option<Box<Expr>> pub(crate) fn replace_id_with_expr<N>(node: &mut N, from: Id, to: Box<Expr>) -> Option<Box<Expr>>