mirror of
https://github.com/swc-project/swc.git
synced 2024-12-27 15:42:51 +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 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.
|
||||||
|
@ -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
|
||||||
|
@ -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>>
|
||||||
|
Loading…
Reference in New Issue
Block a user