mirror of
https://github.com/swc-project/swc.git
synced 2024-12-26 07:02:28 +03:00
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:
parent
c23c07fc50
commit
e37bb55101
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user