From 9925bcb922f8e9467d0b33778d86ae307a2224c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Thu, 13 Jun 2024 13:32:04 +0900 Subject: [PATCH] feat: Reduce the number of parts created by tree shaking (#8472) ### Description Extracted from https://github.com/vercel/turbo/pull/8420 to split diffs for optimization vs intuitiveness ### Testing Instructions --------- Co-authored-by: Tobias Koppers --- .../src/tree_shake/graph.rs | 7 + .../src/tree_shake/mod.rs | 15 + .../tree-shaker/analyzer/grouping/input.js | 13 + .../tree-shaker/analyzer/grouping/output.md | 412 ++++++++++++++++++ 4 files changed, 447 insertions(+) create mode 100644 crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/input.js create mode 100644 crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/output.md diff --git a/crates/turbopack-ecmascript/src/tree_shake/graph.rs b/crates/turbopack-ecmascript/src/tree_shake/graph.rs index c8b5b50dd2f0a..cd19edddaa850 100644 --- a/crates/turbopack-ecmascript/src/tree_shake/graph.rs +++ b/crates/turbopack-ecmascript/src/tree_shake/graph.rs @@ -1124,6 +1124,13 @@ impl DepGraph { .map(|d| matches!(d, Dependency::Strong)) .unwrap_or(false) } + + pub(crate) fn has_path_connecting(&mut self, from: &ItemId, to: &ItemId) -> bool { + let from = self.g.node(from); + let to = self.g.node(to); + + has_path_connecting(&self.g.idx_graph, from, to, None) + } } const ASSERT_CHUNK_KEY: &str = "__turbopack_part__"; diff --git a/crates/turbopack-ecmascript/src/tree_shake/mod.rs b/crates/turbopack-ecmascript/src/tree_shake/mod.rs index f5be2ce7bdbdd..ce0455b40c775 100644 --- a/crates/turbopack-ecmascript/src/tree_shake/mod.rs +++ b/crates/turbopack-ecmascript/src/tree_shake/mod.rs @@ -182,6 +182,21 @@ impl Analyzer<'_> { state .last_writes .retain(|last_write| !self.g.has_strong_dep(item_id, last_write)); + + // Drop all writes which are not reachable from this item. + // + // For + // + // var x = 0 + // x = 1 + // x = 2 + // x += 3 + // + // this will drop `x = 1` as not reachable from `x += 3`. + + state + .last_writes + .retain(|last_write| self.g.has_path_connecting(item_id, last_write)); } // For each var in READ_VARS: diff --git a/crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/input.js b/crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/input.js new file mode 100644 index 0000000000000..9547a7edfc63a --- /dev/null +++ b/crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/input.js @@ -0,0 +1,13 @@ +let x = 1; +x = 2; +x = 3; +x = 4; +x = 5; +x += 6; +x += 7; +x += 8; +x += 9; + +export { x }; + +export const y = x; diff --git a/crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/output.md b/crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/output.md new file mode 100644 index 0000000000000..8885ce526ceb6 --- /dev/null +++ b/crates/turbopack-ecmascript/tests/tree-shaker/analyzer/grouping/output.md @@ -0,0 +1,412 @@ +# Items + +Count: 13 + +## Item 1: Stmt 0, `VarDeclarator(0)` + +```js +let x = 1; + +``` + +- Declares: `x` +- Write: `x` + +## Item 2: Stmt 1, `Normal` + +```js +x = 2; + +``` + +- Write: `x` + +## Item 3: Stmt 2, `Normal` + +```js +x = 3; + +``` + +- Write: `x` + +## Item 4: Stmt 3, `Normal` + +```js +x = 4; + +``` + +- Write: `x` + +## Item 5: Stmt 4, `Normal` + +```js +x = 5; + +``` + +- Write: `x` + +## Item 6: Stmt 5, `Normal` + +```js +x += 6; + +``` + +- Reads: `x` +- Write: `x` + +## Item 7: Stmt 6, `Normal` + +```js +x += 7; + +``` + +- Reads: `x` +- Write: `x` + +## Item 8: Stmt 7, `Normal` + +```js +x += 8; + +``` + +- Reads: `x` +- Write: `x` + +## Item 9: Stmt 8, `Normal` + +```js +x += 9; + +``` + +- Reads: `x` +- Write: `x` + +## Item 10: Stmt 10, `VarDeclarator(0)` + +```js +export const y = x; + +``` + +- Declares: `y` +- Reads: `x` +- Write: `y` + +# Phase 1 +```mermaid +graph TD + Item1; + Item2; + Item3; + Item4; + Item5; + Item6; + Item7; + Item8; + Item9; + Item10; + Item11; + Item11["ModuleEvaluation"]; + Item12; + Item12["export x"]; + Item13; + Item13["export y"]; +``` +# Phase 2 +```mermaid +graph TD + Item1; + Item2; + Item3; + Item4; + Item5; + Item6; + Item7; + Item8; + Item9; + Item10; + Item11; + Item11["ModuleEvaluation"]; + Item12; + Item12["export x"]; + Item13; + Item13["export y"]; + Item6 --> Item5; + Item7 --> Item6; + Item8 --> Item7; + Item9 --> Item8; + Item10 --> Item9; +``` +# Phase 3 +```mermaid +graph TD + Item1; + Item2; + Item3; + Item4; + Item5; + Item6; + Item7; + Item8; + Item9; + Item10; + Item11; + Item11["ModuleEvaluation"]; + Item12; + Item12["export x"]; + Item13; + Item13["export y"]; + Item6 --> Item5; + Item7 --> Item6; + Item8 --> Item7; + Item9 --> Item8; + Item10 --> Item9; +``` +# Phase 4 +```mermaid +graph TD + Item1; + Item2; + Item3; + Item4; + Item5; + Item6; + Item7; + Item8; + Item9; + Item10; + Item11; + Item11["ModuleEvaluation"]; + Item12; + Item12["export x"]; + Item13; + Item13["export y"]; + Item6 --> Item5; + Item7 --> Item6; + Item8 --> Item7; + Item9 --> Item8; + Item10 --> Item9; + Item12 --> Item9; + Item13 --> Item10; +``` +# Final +```mermaid +graph TD + N0["Items: [ItemId(ModuleEvaluation)]"]; + N1["Items: [ItemId(Export(("x", #2), "x"))]"]; + N2["Items: [ItemId(Export(("y", #2), "y")), ItemId(10, VarDeclarator(0))]"]; + N3["Items: [ItemId(4, Normal)]"]; + N4["Items: [ItemId(5, Normal)]"]; + N5["Items: [ItemId(6, Normal)]"]; + N6["Items: [ItemId(7, Normal)]"]; + N7["Items: [ItemId(8, Normal)]"]; + N1 --> N7; + N2 --> N7; + N4 --> N3; + N5 --> N4; + N6 --> N5; + N7 --> N6; +``` +# Entrypoints + +``` +{ + ModuleEvaluation: 0, + Export( + "y", + ): 2, + Export( + "x", + ): 1, +} +``` + + +# Modules (dev) +## Part 0 +```js +"module evaluation"; + +``` +## Part 1 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 7 +}; +export { x as x }; + +``` +## Part 2 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 7 +}; +export { y }; +const y = x; +export { y } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 3 +```js +x = 5; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 4 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 3 +}; +x += 6; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 5 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 4 +}; +x += 7; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 6 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 5 +}; +x += 8; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 7 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 6 +}; +x += 9; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Merged (module eval) +```js +"module evaluation"; + +``` +# Entrypoints + +``` +{ + ModuleEvaluation: 0, + Export( + "y", + ): 2, + Export( + "x", + ): 1, +} +``` + + +# Modules (prod) +## Part 0 +```js +"module evaluation"; + +``` +## Part 1 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 7 +}; +export { x as x }; + +``` +## Part 2 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 7 +}; +export { y }; +const y = x; +export { y } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 3 +```js +x = 5; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 4 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 3 +}; +x += 6; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 5 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 4 +}; +x += 7; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 6 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 5 +}; +x += 8; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Part 7 +```js +import { x } from "__TURBOPACK_PART__" assert { + __turbopack_part__: 6 +}; +x += 9; +export { x } from "__TURBOPACK_VAR__" assert { + __turbopack_var__: true +}; + +``` +## Merged (module eval) +```js +"module evaluation"; + +```