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 super::{
util::{MultiReplacer, MultiReplacerMode},
Optimizer,
};
use super::{util::NormalMultiReplacer, Optimizer};
#[cfg(feature = "debug")]
use crate::debug::dump;
use crate::{
@ -323,16 +320,13 @@ where
#[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>>)
where
N: for<'aa> VisitMutWith<MultiReplacer<'aa>>,
N: for<'aa> VisitMutWith<NormalMultiReplacer<'aa>>,
{
trace_op!("inline: inline_vars_in_node");
n.visit_mut_with(&mut MultiReplacer::new(
&mut vars,
false,
MultiReplacerMode::Normal,
&mut self.changed,
));
let mut v = NormalMultiReplacer::new(&mut vars);
n.visit_mut_with(&mut v);
self.changed |= v.changed;
}
/// Fully inlines iife.

View File

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

View File

@ -4,12 +4,13 @@ use rustc_hash::FxHashMap;
use swc_atoms::js_word;
use swc_common::{Span, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::perf::{Parallel, ParallelExt};
use swc_ecma_utils::{ExprCtx, ExprExt};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use tracing::debug;
use super::{Ctx, Optimizer};
use crate::mode::Mode;
use crate::{mode::Mode, HEAVY_TASK_PARALLELS};
impl<'b, M> Optimizer<'b, M>
where
@ -156,43 +157,28 @@ impl VisitMut for Remapper {
}
}
pub(crate) struct MultiReplacer<'a> {
vars: &'a mut FxHashMap<Id, Box<Expr>>,
clone: bool,
mode: MultiReplacerMode,
worked: &'a mut bool,
#[derive(Clone, Copy)]
pub(crate) struct CloningMultiReplacer<'a> {
pub simple_functions: &'a FxHashMap<Id, Box<Expr>>,
pub lits_for_cmp: &'a FxHashMap<Id, Box<Expr>>,
pub changed: bool,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
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,
}
impl Parallel for CloningMultiReplacer<'_> {
fn create(&self) -> Self {
*self
}
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)?
fn merge(&mut self, other: Self) {
self.changed |= other.changed;
}
}
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);
@ -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 Some(new) = self.var(&i.to_id()) {
if let Some(new) = self.var(&i.to_id(), mode) {
debug!("multi-replacer: Replaced `{}`", i);
*self.worked = true;
self.changed = true;
*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!();
fn visit_mut_callee(&mut self, e: &mut Callee) {
e.visit_mut_children_with(self);
if matches!(self.mode, MultiReplacerMode::OnlyCallee) {
if let Callee::Expr(e) = e {
self.check(e);
}
if let Callee::Expr(e) = e {
self.check(e, MultiReplacerMode::OnlyCallee);
}
}
fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) {
e.visit_mut_children_with(self);
if let MultiReplacerMode::OnlyComparisonWithLit = self.mode {
match e.op {
op!("===") | op!("!==") | op!("==") | op!("!=") => {
//
if e.left.is_lit() {
self.check(&mut e.right);
} else if e.right.is_lit() {
self.check(&mut e.left);
}
match e.op {
op!("===") | op!("!==") | op!("==") | op!("!=") => {
//
if e.left.is_lit() {
self.check(&mut e.right, MultiReplacerMode::OnlyComparisonWithLit);
} else if e.right.is_lit() {
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) {
if self.vars.is_empty() {
return;
@ -262,14 +322,12 @@ impl VisitMut for MultiReplacer<'_> {
return;
}
if matches!(self.mode, MultiReplacerMode::Normal) {
if let Expr::Ident(i) = e {
if let Some(new) = self.var(&i.to_id()) {
debug!("multi-replacer: Replaced `{}`", i);
*self.worked = true;
if let Expr::Ident(i) = e {
if let Some(new) = self.var(&i.to_id()) {
debug!("multi-replacer: Replaced `{}`", i);
self.changed = true;
*e = *new;
}
*e = *new;
}
}
}
@ -290,20 +348,18 @@ impl VisitMut for MultiReplacer<'_> {
fn visit_mut_prop(&mut self, p: &mut Prop) {
p.visit_mut_children_with(self);
if matches!(self.mode, MultiReplacerMode::Normal) {
if let Prop::Shorthand(i) = p {
if let Some(value) = self.var(&i.to_id()) {
debug!("multi-replacer: Replaced `{}` as shorthand", i);
*self.worked = true;
if let Prop::Shorthand(i) = p {
if let Some(value) = self.var(&i.to_id()) {
debug!("multi-replacer: Replaced `{}` as shorthand", i);
self.changed = true;
*p = Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(
i.sym.clone(),
i.span.with_ctxt(Default::default()),
)),
value,
});
}
*p = Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(
i.sym.clone(),
i.span.with_ctxt(Default::default()),
)),
value,
});
}
}
}