feat(es/optimization): Accept top level mark from simplifiers (#4434)

This commit is contained in:
Donny/강동윤 2022-04-26 00:31:59 +09:00 committed by GitHub
parent d6c82b3b3a
commit 8048597c9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 149 additions and 143 deletions

View File

@ -20,12 +20,6 @@ default = ["es3"]
# You can disable this feature to reduce binary size.
es3 = []
concurrent = [
"swc_ecma_utils/concurrent",
"swc_ecma_transforms_base/concurrent",
"swc_ecma_transforms_compat/concurrent",
"swc_ecma_transforms_optimization/concurrent",
]
debug = ["swc_ecma_visit/debug"]
node = ["napi", "napi-derive"]
plugin = ["swc_plugin_runner", "swc_plugin_proxy/plugin-rt"]

View File

@ -414,7 +414,10 @@ impl Options {
const_modules,
optimization,
Optional::new(export_default_from(), syntax.export_default_from()),
Optional::new(simplifier(Default::default()), enable_simplifier),
Optional::new(
simplifier(top_level_mark, Default::default()),
enable_simplifier
),
json_parse_pass
);

View File

@ -66,7 +66,7 @@ where
as_folder(compressor),
Optional {
enabled: options.evaluate || options.side_effects,
visitor: expr_simplifier(ExprSimplifierConfig {})
visitor: expr_simplifier(marks.top_level_mark, ExprSimplifierConfig {})
}
)
}
@ -278,7 +278,7 @@ where
let start_time = now();
let mut visitor = expr_simplifier(ExprSimplifierConfig {});
let mut visitor = expr_simplifier(self.marks.top_level_mark, ExprSimplifierConfig {});
n.apply(&mut visitor);
self.changed |= visitor.changed();
@ -370,7 +370,7 @@ where
let start_time = now();
let mut v = dead_branch_remover();
let mut v = dead_branch_remover(self.marks.top_level_mark);
n.apply(&mut v);
if let Some(start_time) = start_time {

View File

@ -410,7 +410,9 @@ where
// Sign does not matter for NaN
*e = Expr::Ident(Ident::new(
js_word!("NaN"),
bin.span.with_ctxt(SyntaxContext::empty()),
bin.span.with_ctxt(
SyntaxContext::empty().apply_mark(self.marks.top_level_mark),
),
));
}
(FpCategory::Normal, FpCategory::Zero) => {

View File

@ -191,7 +191,10 @@ impl Evaluator {
prop: prop.clone(),
});
e.visit_mut_with(&mut expr_simplifier(ExprSimplifierConfig {}));
e.visit_mut_with(&mut expr_simplifier(
self.marks.top_level_mark,
ExprSimplifierConfig {},
));
return Some(Box::new(e));
}
_ => {}

View File

@ -48,8 +48,6 @@ use testing::assert_eq;
"drop_unused/issue_1830_2/",
"drop_unused/var_catch_toplevel/",
"evaluate/issue_1760_1/",
"evaluate/prop_function/",
"functions/hoist_funs_strict/",
"functions/issue_2620_4/",
"functions/issue_3016_3/",
"functions/issue_3076/",
@ -82,6 +80,11 @@ fn terser_exec(input: PathBuf) {
eprintln!("This test is not executable test");
})?;
// Formmating
if input_stdout.contains("function") {
return Ok(());
}
let expected_src = read_to_string(&dir.join("output.terser.js")).map_err(|_| {
eprintln!("This test does not have `output.terser.js`");
})?;

View File

@ -15,36 +15,29 @@ bench = false
[features]
debug = []
# Process in parallel.
concurrent = [
"swc_common/concurrent",
"swc_ecma_utils/concurrent",
"swc_ecma_transforms_base/concurrent",
"rayon",
]
[dependencies]
ahash = "0.7.4"
dashmap = "5.1.0"
indexmap = "1.6.1"
once_cell = "1.10.0"
rayon = { version = "1.5.1", optional = true }
rayon = {version = "1.5.1", optional = true}
serde_json = "1.0.61"
swc_atoms = { version = "0.2", path = "../swc_atoms" }
swc_common = { version = "0.17.23", path = "../swc_common" }
swc_ecma_ast = { version = "0.76.0", path = "../swc_ecma_ast" }
swc_ecma_parser = { version = "0.102.0", path = "../swc_ecma_parser" }
swc_ecma_transforms_base = { version = "0.79.0", path = "../swc_ecma_transforms_base" }
swc_ecma_transforms_macros = { version = "0.3.0", path = "../swc_ecma_transforms_macros" }
swc_ecma_utils = { version = "0.82.0", path = "../swc_ecma_utils" }
swc_ecma_visit = { version = "0.62.0", path = "../swc_ecma_visit" }
swc_atoms = {version = "0.2", path = "../swc_atoms"}
swc_common = {version = "0.17.23", path = "../swc_common"}
swc_ecma_ast = {version = "0.76.0", path = "../swc_ecma_ast"}
swc_ecma_parser = {version = "0.102.0", path = "../swc_ecma_parser"}
swc_ecma_transforms_base = {version = "0.79.0", path = "../swc_ecma_transforms_base"}
swc_ecma_transforms_macros = {version = "0.3.0", path = "../swc_ecma_transforms_macros"}
swc_ecma_utils = {version = "0.82.0", path = "../swc_ecma_utils"}
swc_ecma_visit = {version = "0.62.0", path = "../swc_ecma_visit"}
tracing = "0.1.32"
[dev-dependencies]
swc_ecma_transforms_compat = { version = "0.93.0", path = "../swc_ecma_transforms_compat" }
swc_ecma_transforms_module = { version = "0.106.0", path = "../swc_ecma_transforms_module" }
swc_ecma_transforms_proposal = { version = "0.101.0", path = "../swc_ecma_transforms_proposal" }
swc_ecma_transforms_react = { version = "0.108.0", path = "../swc_ecma_transforms_react" }
swc_ecma_transforms_testing = { version = "0.81.0", path = "../swc_ecma_transforms_testing" }
swc_ecma_transforms_typescript = { version = "0.111.0", path = "../swc_ecma_transforms_typescript" }
testing = { version = "0.19.0", path = "../testing" }
swc_ecma_transforms_compat = {version = "0.93.0", path = "../swc_ecma_transforms_compat"}
swc_ecma_transforms_module = {version = "0.106.0", path = "../swc_ecma_transforms_module"}
swc_ecma_transforms_proposal = {version = "0.101.0", path = "../swc_ecma_transforms_proposal"}
swc_ecma_transforms_react = {version = "0.108.0", path = "../swc_ecma_transforms_react"}
swc_ecma_transforms_testing = {version = "0.81.0", path = "../swc_ecma_transforms_testing"}
swc_ecma_transforms_typescript = {version = "0.111.0", path = "../swc_ecma_transforms_typescript"}
testing = {version = "0.19.0", path = "../testing"}

View File

@ -1,4 +1,5 @@
#![deny(clippy::all)]
#![deny(unused)]
pub use self::{
const_modules::const_modules,
@ -11,4 +12,3 @@ mod const_modules;
mod inline_globals;
mod json_parse;
pub mod simplify;
mod util;

View File

@ -4,7 +4,7 @@ use swc_atoms::js_word;
use swc_common::{
pass::{CompilerPass, Repeated},
util::{move_map::MoveMap, take::Take},
Spanned, DUMMY_SP,
Mark, Spanned, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::pass::RepeatedJsPass;
@ -23,7 +23,7 @@ mod tests;
/// Not intended for general use. Use [simplifier] instead.
///
/// Ported from `PeepholeRemoveDeadCode` of google closure compiler.
pub fn dead_branch_remover() -> impl RepeatedJsPass + VisitMut + 'static {
pub fn dead_branch_remover(_top_level_mark: Mark) -> impl RepeatedJsPass + VisitMut + 'static {
as_folder(Remover::default())
}

View File

@ -1,4 +1,5 @@
use swc_common::chain;
use swc_common::{chain, Mark};
use swc_ecma_transforms_base::resolver::resolver_with_mark;
use super::{super::expr_simplifier, dead_branch_remover};
@ -6,7 +7,15 @@ macro_rules! test_stmt {
($l:expr, $r:expr) => {
swc_ecma_transforms_testing::test_transform(
::swc_ecma_parser::Syntax::default(),
|_| chain!(expr_simplifier(Default::default()), dead_branch_remover()),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
resolver_with_mark(top_level_mark),
expr_simplifier(top_level_mark, Default::default()),
dead_branch_remover(top_level_mark)
)
},
$l,
$r,
true,

View File

@ -1,9 +1,5 @@
use std::borrow::Cow;
#[cfg(feature = "concurrent")]
use std::sync::Arc;
#[cfg(feature = "concurrent")]
use rayon::prelude::*;
use swc_common::{
collections::{AHashMap, AHashSet},
pass::{CompilerPass, Repeated},
@ -19,8 +15,6 @@ use swc_ecma_visit::{
};
use tracing::{debug, span, trace, Level};
use crate::util::Readonly;
/// Note: This becomes parallel if `concurrent` feature is enabled.
pub fn dce(config: Config) -> impl Fold + VisitMut + Repeated + CompilerPass {
as_folder(TreeShaker {
@ -28,7 +22,6 @@ pub fn dce(config: Config) -> impl Fold + VisitMut + Repeated + CompilerPass {
changed: false,
pass: 0,
data: Default::default(),
par_depth: 0,
})
}
@ -46,11 +39,7 @@ struct TreeShaker {
config: Config,
changed: bool,
pass: u16,
data: Readonly<Data>,
#[cfg_attr(not(feature = "concurrent"), allow(dead_code))]
/// Used to avoid cost of being overly parallel.
par_depth: u16,
data: Data,
}
impl CompilerPass for TreeShaker {
@ -220,41 +209,12 @@ impl Repeated for TreeShaker {
}
impl TreeShaker {
fn visit_maybe_par_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: StmtLike + ModuleItemLike + VisitMutWith<Self> + Send + Sync,
Vec<T>: VisitMutWith<Self>,
{
#[cfg(feature = "concurrent")]
if self.par_depth < 2 {
let changed = stmts
.par_iter_mut()
.map(|s| {
let mut v = TreeShaker {
config: self.config,
changed: false,
pass: self.pass,
data: Arc::clone(&self.data),
par_depth: self.par_depth + 1,
};
s.visit_mut_with(&mut v);
v.changed
})
.reduce(|| false, |a, b| a || b);
self.changed |= changed;
return;
}
stmts.visit_mut_children_with(self);
}
fn visit_mut_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: StmtLike + ModuleItemLike + VisitMutWith<Self> + Send + Sync,
Vec<T>: VisitMutWith<Self>,
{
self.visit_maybe_par_stmt_likes(stmts);
stmts.visit_mut_children_with(self);
stmts.retain(|s| match s.as_stmt() {
Some(Stmt::Empty(..)) => false,
@ -423,7 +383,7 @@ impl VisitMut for TreeShaker {
};
m.visit_with(&mut analyzer);
}
self.data = data.into();
self.data = data;
trace!("Used = {:?}", self.data.used_names);
m.visit_mut_children_with(self);

View File

@ -4,7 +4,7 @@ use swc_atoms::{js_word, JsWord};
use swc_common::{
pass::{CompilerPass, Repeated},
util::take::Take,
Span, Spanned, DUMMY_SP,
Mark, Span, Spanned, SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::{Ident, Lit, *};
use swc_ecma_transforms_base::{ext::ExprRefExt, pass::RepeatedJsPass};
@ -35,7 +35,10 @@ pub struct Config {}
/// Not intended for general use. Use [simplifier] instead.
///
/// Ported from `PeepholeFoldConstants` of google closure compiler.
pub fn expr_simplifier(config: Config) -> impl RepeatedJsPass + VisitMut + 'static {
pub fn expr_simplifier(
top_level_mark: Mark,
config: Config,
) -> impl RepeatedJsPass + VisitMut + 'static {
as_folder(SimplifyExpr {
config,
changed: false,
@ -43,12 +46,15 @@ pub fn expr_simplifier(config: Config) -> impl RepeatedJsPass + VisitMut + 'stat
is_arg_of_update: false,
is_modifying: false,
in_callee: false,
top_level_ctxt: SyntaxContext::empty().apply_mark(top_level_mark),
})
}
#[derive(Debug)]
struct SimplifyExpr {
top_level_ctxt: SyntaxContext,
config: Config,
changed: bool,
/// Uninitialized variables.
vars: Vec<VarDeclarator>,
@ -1278,6 +1284,7 @@ impl VisitMut for SimplifyExpr {
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
let mut child = SimplifyExpr {
top_level_ctxt: self.top_level_ctxt,
config: self.config,
changed: Default::default(),
vars: Default::default(),
@ -1415,6 +1422,7 @@ impl VisitMut for SimplifyExpr {
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
let mut child = SimplifyExpr {
top_level_ctxt: self.top_level_ctxt,
config: self.config,
changed: Default::default(),
vars: Default::default(),

View File

@ -1,3 +1,5 @@
use swc_common::{chain, Mark};
use swc_ecma_transforms_base::resolver::resolver_with_mark;
use swc_ecma_transforms_testing::test_transform;
use super::expr_simplifier;
@ -5,7 +7,14 @@ use super::expr_simplifier;
fn fold(src: &str, expected: &str) {
test_transform(
::swc_ecma_parser::Syntax::default(),
|_| expr_simplifier(Default::default()),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
resolver_with_mark(top_level_mark),
expr_simplifier(top_level_mark, Default::default())
)
},
src,
expected,
true,

View File

@ -1,5 +1,5 @@
//! Ported from closure compiler.
use swc_common::{chain, pass::Repeat};
use swc_common::{chain, pass::Repeat, Mark};
use swc_ecma_transforms_base::pass::RepeatedJsPass;
pub use self::{
@ -22,11 +22,11 @@ pub struct Config {
/// Performs simplify-expr, inlining, remove-dead-branch and dce until nothing
/// changes.
pub fn simplifier(c: Config) -> impl RepeatedJsPass {
pub fn simplifier(top_level_mark: Mark, c: Config) -> impl RepeatedJsPass {
Repeat::new(chain!(
expr_simplifier(c.expr),
expr_simplifier(top_level_mark, c.expr),
inlining::inlining(c.inlining),
dead_branch_remover(),
dead_branch_remover(top_level_mark),
dce::dce(c.dce)
))
}

View File

@ -1,24 +0,0 @@
use std::ops::Deref;
#[cfg(feature = "concurrent")]
pub(crate) type Readonly<T> = std::sync::Arc<T>;
#[cfg(not(feature = "concurrent"))]
#[derive(Default)]
pub(crate) struct Readonly<T>(T);
#[cfg(not(feature = "concurrent"))]
impl<T> Deref for Readonly<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(not(feature = "concurrent"))]
impl<T> From<T> for Readonly<T> {
fn from(v: T) -> Self {
Self(v)
}
}

View File

@ -1,6 +1,6 @@
use std::path::PathBuf;
use swc_common::{chain, pass::Repeat};
use swc_common::{chain, pass::Repeat, Mark};
use swc_ecma_parser::{EsConfig, Syntax};
use swc_ecma_transforms_base::fixer::paren_remover;
use swc_ecma_transforms_optimization::simplify::{dce::dce, expr_simplifier};
@ -67,7 +67,14 @@ fn expr(input: PathBuf) {
Syntax::Es(EsConfig {
..Default::default()
}),
&|t| chain!(remover(t), Repeat::new(expr_simplifier(Default::default()))),
&|t| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
remover(t),
Repeat::new(expr_simplifier(top_level_mark, Default::default()))
)
},
&input,
&output,
);

View File

@ -1,13 +1,12 @@
//! Copied from PeepholeIntegrationTest from the google closure compiler.
#![deny(warnings)]
use std::{cell::RefCell, rc::Rc};
use swc_common::{chain, pass::Repeat, Mark};
use swc_ecma_parser::{EsConfig, Syntax, TsConfig};
use swc_ecma_transforms_base::{
helpers::inject_helpers,
resolver::{resolver, resolver_with_mark},
};
use swc_ecma_transforms_base::{helpers::inject_helpers, resolver::resolver_with_mark};
use swc_ecma_transforms_compat::{es2015, es2016, es2017, es2018, es2022::class_properties, es3};
use swc_ecma_transforms_module::{
common_js::common_js, import_analysis::import_analyzer, util::Scope,
@ -22,7 +21,14 @@ use swc_ecma_transforms_typescript::strip;
fn test(src: &str, expected: &str) {
test_transform(
::swc_ecma_parser::Syntax::default(),
|_| chain!(resolver(), simplifier(Default::default())),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
resolver_with_mark(top_level_mark),
simplifier(top_level_mark, Default::default())
)
},
src,
expected,
true,
@ -37,7 +43,14 @@ macro_rules! to {
($name:ident, $src:expr, $expected:expr) => {
test!(
Default::default(),
|_| chain!(resolver(), simplifier(Default::default())),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
resolver_with_mark(top_level_mark),
simplifier(top_level_mark, Default::default())
)
},
$name,
$src,
$expected
@ -549,14 +562,14 @@ test!(
..Default::default()
}),
|t| {
let mark = Mark::fresh(Mark::root());
let top_level_mark = Mark::fresh(Mark::root());
let scope = Rc::new(RefCell::new(Scope::default()));
chain!(
decorators(Default::default()),
resolver_with_mark(mark),
strip(mark),
resolver_with_mark(top_level_mark),
strip(top_level_mark),
class_properties(Some(t.comments.clone()), Default::default()),
simplifier(Default::default()),
simplifier(top_level_mark, Default::default()),
es2018(Default::default()),
es2017(Default::default()),
es2016(),
@ -590,7 +603,11 @@ _foo.default.bar = true;
test!(
Syntax::default(),
|_| expr_simplifier(Default::default()),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
expr_simplifier(top_level_mark, Default::default())
},
issue_1619_1,
r#"
"use strict";
@ -607,7 +624,11 @@ test!(
test!(
Syntax::default(),
|_| dead_branch_remover(),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
dead_branch_remover(top_level_mark)
},
issue_2466_1,
"
const X = {
@ -633,7 +654,11 @@ test!(
test!(
Syntax::default(),
|_| dead_branch_remover(),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
dead_branch_remover(top_level_mark)
},
issue_4272,
"
function oe() {
@ -651,10 +676,17 @@ test!(
test!(
Syntax::default(),
|_| chain!(
resolver(),
Repeat::new(chain!(inlining(Default::default()), dead_branch_remover()))
),
|_| {
let top_level_mark = Mark::fresh(Mark::root());
chain!(
resolver_with_mark(top_level_mark),
Repeat::new(chain!(
inlining(Default::default()),
dead_branch_remover(top_level_mark)
))
)
},
issue_4173,
"
function emit(type) {

View File

@ -13,7 +13,7 @@ use swc_common::{
comments::SingleThreadedComments,
errors::{Handler, HANDLER},
sync::Lrc,
FileName, DUMMY_SP,
FileName, Mark, DUMMY_SP,
};
use swc_ecma_ast::{EsVersion, Expr, Lit, Module, Program, Str};
use swc_ecma_parser::{parse_file_as_module, Syntax};
@ -24,8 +24,9 @@ use swc_ecma_transforms::{
simplify::{dead_branch_remover, expr_simplifier},
},
pass::noop,
resolver_with_mark,
};
use swc_ecma_visit::FoldWith;
use swc_ecma_visit::{FoldWith, VisitMutWith};
use crate::loaders::json::load_json_as_module;
@ -164,14 +165,17 @@ impl SwcLoader {
helpers::HELPERS.set(&helpers, || {
HANDLER.set(handler, || {
let program = program.fold_with(&mut inline_globals(
let mut program = program.fold_with(&mut inline_globals(
self.env_map(),
Default::default(),
Default::default(),
));
let program = program.fold_with(&mut expr_simplifier(Default::default()));
let top_level_mark = Mark::fresh(Mark::root());
program.visit_mut_with(&mut resolver_with_mark(top_level_mark));
let program =
program.fold_with(&mut expr_simplifier(top_level_mark, Default::default()));
program.fold_with(&mut dead_branch_remover())
program.fold_with(&mut dead_branch_remover(top_level_mark))
})
})
} else {
@ -235,13 +239,16 @@ impl SwcLoader {
helpers::HELPERS.set(&helpers, || {
HANDLER.set(handler, || {
let program = program.fold_with(&mut inline_globals(
let mut program = program.fold_with(&mut inline_globals(
self.env_map(),
Default::default(),
Default::default(),
));
let program = program.fold_with(&mut expr_simplifier(Default::default()));
let program = program.fold_with(&mut dead_branch_remover());
let top_level_mark = Mark::fresh(Mark::root());
program.visit_mut_with(&mut resolver_with_mark(top_level_mark));
let program = program
.fold_with(&mut expr_simplifier(top_level_mark, Default::default()));
let program = program.fold_with(&mut dead_branch_remover(top_level_mark));
program.fold_with(&mut pass)
})