From 5099f66abe610cf3f4d551cc864212670d4085f6 Mon Sep 17 00:00:00 2001 From: Graham Kelly Date: Tue, 29 Jul 2025 16:05:36 +0000 Subject: [PATCH 1/3] expand if statements before compression to allow iife inlining --- .../src/compress/optimize/conditionals.rs | 42 ++++++++++++++++++- .../src/compress/optimize/mod.rs | 2 + 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs index 39eaa67ab73e..15a3bd1de2e4 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs @@ -1,4 +1,4 @@ -use std::mem::swap; +use std::mem::{swap, take}; use swc_common::{util::take::Take, EqIgnoreSpan, Spanned, SyntaxContext, DUMMY_SP}; use swc_ecma_ast::*; @@ -184,6 +184,46 @@ impl Optimizer<'_> { *stmts = new; } + pub(super) fn expand_if_stmt_from_cond(&mut self, s: &mut Stmt) { + loop { + if let Stmt::Return(r) = s { + if let Some(Expr::Cond(c)) = r.arg.as_deref_mut() { + *s = Stmt::If(IfStmt { + span: c.span, + test: take(&mut c.test), + cons: Box::new(Stmt::Return(ReturnStmt { + span: r.span, + arg: Some(take(&mut c.cons)), + })), + alt: Some(Box::new(Stmt::Return(ReturnStmt { + span: r.span, + arg: Some(take(&mut c.alt)), + }))), + }); + continue; + } + } + if let Stmt::Expr(e) = s { + if let Expr::Cond(c) = &mut *e.expr { + *s = Stmt::If(IfStmt { + span: c.span, + test: take(&mut c.test), + cons: Box::new(Stmt::Expr(ExprStmt { + span: e.span, + expr: take(&mut c.cons), + })), + alt: Some(Box::new(Stmt::Expr(ExprStmt { + span: e.span, + expr: take(&mut c.alt), + }))), + }); + continue; + } + } + break; + } + } + /// /// # Examples /// diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs index 05b280a00be9..96ed2e3514f1 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs @@ -2401,6 +2401,8 @@ impl VisitMut for Optimizer<'_> { } }; + self.expand_if_stmt_from_cond(s); + let ctx = self .ctx .clone() From bbc5fc86ffdae7e4a3d13afe59bcb4cd884452f3 Mon Sep 17 00:00:00 2001 From: Graham Kelly Date: Tue, 29 Jul 2025 17:32:05 +0000 Subject: [PATCH 2/3] fix test, gate implementation, and ignore spurious changes --- .../src/compress/optimize/conditionals.rs | 41 +++++++++++-------- .../src/compress/optimize/mod.rs | 4 +- .../simple/if/unfolding-refolding/input.js | 1 + .../simple/if/unfolding-refolding/output.js | 2 + 4 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/input.js create mode 100644 crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/output.js diff --git a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs index 15a3bd1de2e4..49feebec20a3 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs @@ -184,10 +184,12 @@ impl Optimizer<'_> { *stmts = new; } - pub(super) fn expand_if_stmt_from_cond(&mut self, s: &mut Stmt) { - loop { + pub(super) fn expand_if_stmt_from_cond(&mut self, s: &mut Stmt) -> bool { + let mut changed = false; + if self.options.conditionals { if let Stmt::Return(r) = s { if let Some(Expr::Cond(c)) = r.arg.as_deref_mut() { + changed = true; *s = Stmt::If(IfStmt { span: c.span, test: take(&mut c.test), @@ -200,11 +202,12 @@ impl Optimizer<'_> { arg: Some(take(&mut c.alt)), }))), }); - continue; + // continue; } } if let Stmt::Expr(e) = s { if let Expr::Cond(c) = &mut *e.expr { + changed = true; *s = Stmt::If(IfStmt { span: c.span, test: take(&mut c.test), @@ -217,11 +220,11 @@ impl Optimizer<'_> { expr: take(&mut c.alt), }))), }); - continue; + // continue; } } - break; } + changed } /// @@ -246,7 +249,7 @@ impl Optimizer<'_> { /// some_condition ? side_effects(x) : side_effects(y); /// } /// ``` - pub(super) fn compress_if_stmt_as_cond(&mut self, s: &mut Stmt) { + pub(super) fn compress_if_stmt_as_cond(&mut self, s: &mut Stmt, change: bool) { let stmt = match s { Stmt::If(v) => v, _ => return, @@ -328,12 +331,12 @@ impl Optimizer<'_> { None => return, }; - let new_expr = self.compress_similar_cons_alt(&mut stmt.test, cons, alt, true); + let new_expr = self.compress_similar_cons_alt(&mut stmt.test, cons, alt, true, change); if let Some(v) = new_expr { debug_assert_valid(&v); - self.changed = true; + self.changed = self.changed || change; report_change!("conditionals: Merging cons and alt as only one argument differs"); *s = ExprStmt { span: stmt.span, @@ -349,7 +352,7 @@ impl Optimizer<'_> { "Compressing if statement as conditional expression (even though cons and alt is \ not compressable)" ); - self.changed = true; + self.changed = self.changed || change; *s = ExprStmt { span: stmt.span, expr: CondExpr { @@ -375,8 +378,13 @@ impl Optimizer<'_> { _ => return, }; - let compressed = - self.compress_similar_cons_alt(&mut cond.test, &mut cond.cons, &mut cond.alt, false); + let compressed = self.compress_similar_cons_alt( + &mut cond.test, + &mut cond.cons, + &mut cond.alt, + false, + true, + ); if let Some(v) = compressed { *e = v; @@ -404,6 +412,7 @@ impl Optimizer<'_> { cons: &mut Expr, alt: &mut Expr, is_for_if_stmt: bool, + change: bool, ) -> Option { debug_assert_valid(cons); debug_assert_valid(alt); @@ -478,7 +487,7 @@ impl Optimizer<'_> { report_change!( "conditionals: Merging cons and alt as only one argument differs" ); - self.changed = true; + self.changed |= change; let mut new_args = Vec::new(); @@ -721,7 +730,7 @@ impl Optimizer<'_> { // => // (z && condition(), "fuji"); (Expr::Seq(cons), alt) if (**cons.exprs.last().unwrap()).eq_ignore_span(&*alt) => { - self.changed = true; + self.changed |= change; report_change!("conditionals: Reducing seq expr in cons"); // cons.exprs.pop(); @@ -762,7 +771,7 @@ impl Optimizer<'_> { if idx == 0 { None } else if idx == left_len { - self.changed = true; + self.changed |= change; report_change!("conditionals: Reducing similar seq expr in cons"); let mut alt = right.exprs.take(); @@ -785,7 +794,7 @@ impl Optimizer<'_> { .into(), ) } else if idx == right_len { - self.changed = true; + self.changed |= change; report_change!("conditionals: Reducing similar seq expr in alt"); let mut cons = left.exprs.take(); @@ -808,7 +817,7 @@ impl Optimizer<'_> { .into(), ) } else { - self.changed = true; + self.changed |= change; report_change!("conditionals: Reducing similar seq expr"); let _ = left.exprs.split_off(left_len - idx); let mut common = right.exprs.split_off(right_len - idx); diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs index 96ed2e3514f1..06e254947670 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs @@ -2401,7 +2401,7 @@ impl VisitMut for Optimizer<'_> { } }; - self.expand_if_stmt_from_cond(s); + let change = !self.expand_if_stmt_from_cond(s); let ctx = self .ctx @@ -2610,7 +2610,7 @@ impl VisitMut for Optimizer<'_> { debug_assert_eq!(self.append_stmts.len(), append_len); debug_assert_valid(s); - self.compress_if_stmt_as_cond(s); + self.compress_if_stmt_as_cond(s, change); debug_assert_eq!(self.prepend_stmts.len(), prepend_len); debug_assert_eq!(self.append_stmts.len(), append_len); diff --git a/crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/input.js b/crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/input.js new file mode 100644 index 000000000000..606d2d5ae088 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/input.js @@ -0,0 +1 @@ +return a ? b : (d => d)(c) \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/output.js b/crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/output.js new file mode 100644 index 000000000000..9a201af4fd5e --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/simple/if/unfolding-refolding/output.js @@ -0,0 +1,2 @@ +if (a) return b; +return c; From 0c815380e293afd2ba29db5341fbc30dc2ba869a Mon Sep 17 00:00:00 2001 From: Graham Kelly Date: Tue, 29 Jul 2025 17:35:31 +0000 Subject: [PATCH 3/3] changing --- .../src/compress/optimize/conditionals.rs | 39 ++++++++----------- .../src/compress/optimize/mod.rs | 4 +- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs index 49feebec20a3..e6c9f935361c 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs @@ -184,12 +184,12 @@ impl Optimizer<'_> { *stmts = new; } - pub(super) fn expand_if_stmt_from_cond(&mut self, s: &mut Stmt) -> bool { - let mut changed = false; + pub(super) fn expand_if_stmt_from_cond(&mut self, s: &mut Stmt) { + // let mut changed = false; if self.options.conditionals { if let Stmt::Return(r) = s { if let Some(Expr::Cond(c)) = r.arg.as_deref_mut() { - changed = true; + // changed = true; *s = Stmt::If(IfStmt { span: c.span, test: take(&mut c.test), @@ -207,7 +207,7 @@ impl Optimizer<'_> { } if let Stmt::Expr(e) = s { if let Expr::Cond(c) = &mut *e.expr { - changed = true; + // changed = true; *s = Stmt::If(IfStmt { span: c.span, test: take(&mut c.test), @@ -224,7 +224,7 @@ impl Optimizer<'_> { } } } - changed + // changed } /// @@ -249,7 +249,7 @@ impl Optimizer<'_> { /// some_condition ? side_effects(x) : side_effects(y); /// } /// ``` - pub(super) fn compress_if_stmt_as_cond(&mut self, s: &mut Stmt, change: bool) { + pub(super) fn compress_if_stmt_as_cond(&mut self, s: &mut Stmt) { let stmt = match s { Stmt::If(v) => v, _ => return, @@ -331,12 +331,12 @@ impl Optimizer<'_> { None => return, }; - let new_expr = self.compress_similar_cons_alt(&mut stmt.test, cons, alt, true, change); + let new_expr = self.compress_similar_cons_alt(&mut stmt.test, cons, alt, true); if let Some(v) = new_expr { debug_assert_valid(&v); - self.changed = self.changed || change; + self.changed = true; report_change!("conditionals: Merging cons and alt as only one argument differs"); *s = ExprStmt { span: stmt.span, @@ -352,7 +352,7 @@ impl Optimizer<'_> { "Compressing if statement as conditional expression (even though cons and alt is \ not compressable)" ); - self.changed = self.changed || change; + self.changed = true; *s = ExprStmt { span: stmt.span, expr: CondExpr { @@ -378,13 +378,8 @@ impl Optimizer<'_> { _ => return, }; - let compressed = self.compress_similar_cons_alt( - &mut cond.test, - &mut cond.cons, - &mut cond.alt, - false, - true, - ); + let compressed = + self.compress_similar_cons_alt(&mut cond.test, &mut cond.cons, &mut cond.alt, false); if let Some(v) = compressed { *e = v; @@ -412,7 +407,7 @@ impl Optimizer<'_> { cons: &mut Expr, alt: &mut Expr, is_for_if_stmt: bool, - change: bool, + // change: bool, ) -> Option { debug_assert_valid(cons); debug_assert_valid(alt); @@ -487,7 +482,7 @@ impl Optimizer<'_> { report_change!( "conditionals: Merging cons and alt as only one argument differs" ); - self.changed |= change; + self.changed = true; let mut new_args = Vec::new(); @@ -730,7 +725,7 @@ impl Optimizer<'_> { // => // (z && condition(), "fuji"); (Expr::Seq(cons), alt) if (**cons.exprs.last().unwrap()).eq_ignore_span(&*alt) => { - self.changed |= change; + self.changed = true; report_change!("conditionals: Reducing seq expr in cons"); // cons.exprs.pop(); @@ -771,7 +766,7 @@ impl Optimizer<'_> { if idx == 0 { None } else if idx == left_len { - self.changed |= change; + self.changed = true; report_change!("conditionals: Reducing similar seq expr in cons"); let mut alt = right.exprs.take(); @@ -794,7 +789,7 @@ impl Optimizer<'_> { .into(), ) } else if idx == right_len { - self.changed |= change; + self.changed = true; report_change!("conditionals: Reducing similar seq expr in alt"); let mut cons = left.exprs.take(); @@ -817,7 +812,7 @@ impl Optimizer<'_> { .into(), ) } else { - self.changed |= change; + self.changed = true; report_change!("conditionals: Reducing similar seq expr"); let _ = left.exprs.split_off(left_len - idx); let mut common = right.exprs.split_off(right_len - idx); diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs index 06e254947670..96ed2e3514f1 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs @@ -2401,7 +2401,7 @@ impl VisitMut for Optimizer<'_> { } }; - let change = !self.expand_if_stmt_from_cond(s); + self.expand_if_stmt_from_cond(s); let ctx = self .ctx @@ -2610,7 +2610,7 @@ impl VisitMut for Optimizer<'_> { debug_assert_eq!(self.append_stmts.len(), append_len); debug_assert_valid(s); - self.compress_if_stmt_as_cond(s, change); + self.compress_if_stmt_as_cond(s); debug_assert_eq!(self.prepend_stmts.len(), prepend_len); debug_assert_eq!(self.append_stmts.len(), append_len);