From 0a2f68756f38f8ef003425e856061502b0c97a0a Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:22:00 +0000 Subject: [PATCH] refactor(minifier): move dce conditional expression to `RemoveDeadCode` (#5971) This is aligned to closure compiler --- .../src/ast_passes/peephole_fold_constants.rs | 6 +++ .../peephole_minimize_conditions.rs | 38 +++----------- .../ast_passes/peephole_remove_dead_code.rs | 51 ++++++++++++------- crates/oxc_minifier/src/compressor.rs | 17 +++++-- .../coverage/snapshots/minifier_test262.snap | 11 +--- 5 files changed, 59 insertions(+), 64 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 505ffe374379f..84629360a9915 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -113,7 +113,13 @@ impl<'a> PeepholeFoldConstants { match expr.operator { UnaryOperator::Void => Self::try_reduce_void(expr, ctx), UnaryOperator::Typeof => self.try_fold_type_of(expr, ctx), + #[allow(clippy::float_cmp)] UnaryOperator::LogicalNot => { + if let Expression::NumericLiteral(n) = &expr.argument { + if n.value == 0.0 || n.value == 1.0 { + return None; + } + } expr.argument.to_boolean().map(|b| ctx.ast.expression_boolean_literal(SPAN, !b)) } // `-NaN` -> `NaN` diff --git a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs index 660667ac81a11..a447c9df706aa 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -1,7 +1,7 @@ use oxc_ast::ast::*; -use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; +use oxc_traverse::{Traverse, TraverseCtx}; -use crate::{node_util::NodeUtil, tri::Tri, CompressorPass}; +use crate::CompressorPass; /// Minimize Conditions /// @@ -16,44 +16,18 @@ impl<'a> CompressorPass<'a> for PeepholeMinimizeConditions {} impl<'a> Traverse<'a> for PeepholeMinimizeConditions { fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.fold_expression(expr, ctx); - } -} - -impl<'a> PeepholeMinimizeConditions { - pub fn new() -> Self { - Self - } - - fn fold_expression(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { if let Some(folded_expr) = match expr { - Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx), Expression::UnaryExpression(e) if e.operator.is_not() => self.try_minimize_not(e, ctx), _ => None, } { *expr = folded_expr; }; } +} - fn try_fold_conditional_expression( - &self, - expr: &mut ConditionalExpression<'a>, - ctx: &mut TraverseCtx<'a>, - ) -> Option> { - match ctx.get_boolean_value(&expr.test) { - Tri::True => { - // Bail `let o = { f() { assert.ok(this !== o); } }; (true ? o.f : false)(); (true ? o.f : false)``;` - let parent = ctx.ancestry.parent(); - if parent.is_tagged_template_expression() - || matches!(parent, Ancestor::CallExpressionCallee(_)) - { - return None; - } - Some(ctx.ast.move_expression(&mut expr.consequent)) - } - Tri::False => Some(ctx.ast.move_expression(&mut expr.alternate)), - Tri::Unknown => None, - } +impl<'a> PeepholeMinimizeConditions { + pub fn new() -> Self { + Self } /// Try to minimize NOT nodes such as `!(x==y)`. diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index 28fad6d6f1b89..a1939af0b9181 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -1,7 +1,7 @@ use oxc_allocator::Vec; use oxc_ast::{ast::*, Visit}; use oxc_span::SPAN; -use oxc_traverse::{Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; use crate::{keep_var::KeepVar, node_util::NodeUtil, tri::Tri, CompressorPass}; @@ -23,6 +23,15 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); self.dead_code_elimination(stmts, ctx); } + + fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + if let Some(folded_expr) = match expr { + Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx), + _ => None, + } { + *expr = folded_expr; + } + } } impl<'a> PeepholeRemoveDeadCode { @@ -114,22 +123,26 @@ impl<'a> PeepholeRemoveDeadCode { Tri::Unknown => {} } } -} - -// /// -// #[cfg(test)] -// mod test { -// use oxc_allocator::Allocator; - -// use crate::{tester, CompressOptions}; - -// fn test(source_text: &str, expected: &str) { -// let allocator = Allocator::default(); -// let mut pass = super::PeepholeRemoveDeadCode::new(); -// tester::test(&allocator, source_text, expected, &mut pass); -// } -// fn test_same(source_text: &str) { -// test(source_text, source_text); -// } -// } + /// Try folding conditional expression (?:) if the condition results of the condition is known. + fn try_fold_conditional_expression( + &self, + expr: &mut ConditionalExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + match ctx.get_boolean_value(&expr.test) { + Tri::True => { + // Bail `let o = { f() { assert.ok(this !== o); } }; (true ? o.f : false)(); (true ? o.f : false)``;` + let parent = ctx.ancestry.parent(); + if parent.is_tagged_template_expression() + || matches!(parent, Ancestor::CallExpressionCallee(_)) + { + return None; + } + Some(ctx.ast.move_expression(&mut expr.consequent)) + } + Tri::False => Some(ctx.ast.move_expression(&mut expr.alternate)), + Tri::Unknown => None, + } + } +} diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index 496085f4ae2e2..00b85cd3b7e51 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -42,13 +42,24 @@ impl<'a> Compressor<'a> { return; } - self.fold_constants(program, &mut ctx); + // earlyPeepholeOptimizations + // TODO: MinimizeExitPoints self.minimize_conditions(program, &mut ctx); + self.substitute_alternate_syntax(program, &mut ctx); + // TODO: PeepholeReplaceKnownMethods + self.remove_dead_code(program, &mut ctx); + self.fold_constants(program, &mut ctx); + + // latePeepholeOptimizations + // TODO: StatementFusion self.remove_dead_code(program, &mut ctx); - // self.statement_fusion(program, &mut ctx); + self.minimize_conditions(program, &mut ctx); self.substitute_alternate_syntax(program, &mut ctx); - self.collapse_variable_declarations(program, &mut ctx); + // TODO: PeepholeReplaceKnownMethods + self.fold_constants(program, &mut ctx); + self.exploit_assigns(program, &mut ctx); + self.collapse_variable_declarations(program, &mut ctx); } fn dead_code_elimination(self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { diff --git a/tasks/coverage/snapshots/minifier_test262.snap b/tasks/coverage/snapshots/minifier_test262.snap index 0a8004b0abf96..9b320b73fdbda 100644 --- a/tasks/coverage/snapshots/minifier_test262.snap +++ b/tasks/coverage/snapshots/minifier_test262.snap @@ -2,13 +2,4 @@ commit: d62fa93c minifier_test262 Summary: AST Parsed : 43765/43765 (100.00%) -Positive Passed: 43756/43765 (99.98%) -Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A3_T2.js -Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A3_T3.js -Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A3_T4.js -Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A4_T2.js -Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A4_T3.js -Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A4_T4.js -Compress: tasks/coverage/test262/test/language/statements/if/S12.5_A12_T1.js -Compress: tasks/coverage/test262/test/language/statements/if/S12.5_A12_T3.js -Compress: tasks/coverage/test262/test/language/statements/if/S12.5_A12_T4.js +Positive Passed: 43765/43765 (100.00%)