Skip to content

Replace stripDefaultUndefined with Terser --compress - #27926

Merged
sbc100 merged 1 commit into
emscripten-core:mainfrom
sbc100:terser-compress
Oct 9, 2026
Merged

sbc100 merged 1 commit into
emscripten-core:mainfrom
sbc100:terser-compress

Conversation

@sbc100

@sbc100 sbc100 commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Instead of a dedicated stripDefaultUndefined Acorn AST pass (added in
compression ({defaults: false, evaluate: true, keep_fargs: false})
when minifying JS without Closure Compiler. In addition to stripping
= undefined default parameter values, this performs lightweight Terser
cleanups (undefined -> void 0, single-statement block brace
removal, and constant folding).

In general, this prevents us from needing to replicate compression
passes that already exist in upstream Terser (see #27871).

Timing highlights (test/hello_world.c with -sINCLUDE_FULL_LIBRARY,
~252 KB minified JS):

  • minify_sync with compress: false: 105.5 ms
  • minify_sync with compress: {defaults: false, evaluate: true, keep_fargs: false}: 188.3 ms (+82.8 ms, or ~+63 ms net after
    removing the stripDefaultUndefined AST walk), saving ~1.4 KB.
  • On a standard -O2 hello_world.c build (~8.5 KB JS), minify_sync
    goes from 3.2 ms to 5.5 ms (+2.3 ms).

This change, in general, should prevent us from being tempted to
replicate compression passes that already exist in upstream terser
(See #27871, for example).

See: #27839

@sbc100
sbc100 requested a review from kripken October 8, 2026 15:37
Comment thread test/codesize/test_codesize_file_preload.expected.js
@sbc100
sbc100 requested a review from dschuff October 8, 2026 17:39
Comment thread test/test_other.py

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

This looks right to me. Maybe the only question I have is if we want to run --compress in -O2 or just -O3 (I would lean towards not in -O2).

Comment thread tools/building.py
@sbc100
sbc100 force-pushed the terser-compress branch 2 times, most recently from e8578dc to 2a0a2d8 Compare October 8, 2026 22:04
sbc100 added a commit that referenced this pull request Oct 8, 2026
Previously, at `-O3`/`-Os`/`-Oz` (`expensive_optimizations`),
`minify_wasm_js` invoked `acorn-optimizer.mjs` up to four separate times
to parse and reprint JS:
1. Pre-`metadce` (`AJSDCE`, `stripDefaultUndefined`,
`--minify-whitespace`)
2. Inside `metadce` (`applyDCEGraphRemovals`, `--minify-whitespace`)
3. Post-`metadce` (`AJSDCE`, `--minify-whitespace`)
4. Inside `minify_wasm_imports_and_exports`
(`applyImportAndExportNameChanges`, `--minify-whitespace`)

Have `metadce` and `minify_wasm_imports_and_exports` return their
removal/renaming metadata instead of invoking `acorn_optimizer`
directly, and consolidate `applyDCEGraphRemovals`, post-`metadce`
`AJSDCE`, `applyImportAndExportNameChanges`, `stripDefaultUndefined`,
and `--minify-whitespace` into a single final `acorn_optimizer` pass.

Split out from #27926
@sbc100

sbc100 commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Nice!

This looks right to me. Maybe the only question I have is if we want to run --compress in -O2 or just -O3 (I would lean towards not in -O2).

Really? Why? Do you expect -O2 output to be readable? Remember can always add -O2 --minify=0 if you prefer.

@sbc100

sbc100 commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Also, we also run whitespace minification on main on -O2.. so changing that to not minify now seems odd.

@sbc100

sbc100 commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased on top of #27928 .. much smaller change now

Instead of a dedicated `stripDefaultUndefined` Acorn AST pass (added in
compression (`{defaults: false, evaluate: true, keep_fargs: false}`)
when minifying JS without Closure Compiler. In addition to stripping
`= undefined` default parameter values, this performs lightweight Terser
cleanups (`undefined` -> `void 0`, single-statement block brace
removal, and constant folding).

In general, this prevents us from needing to replicate compression
passes that already exist in upstream Terser (see emscripten-core#27871).

Timing highlights (`test/hello_world.c` with `-sINCLUDE_FULL_LIBRARY`,
~252 KB minified JS):
- `minify_sync` with `compress: false`: 105.5 ms
- `minify_sync` with `compress: {defaults: false, evaluate: true,
  keep_fargs: false}`: 188.3 ms (+82.8 ms, or ~+63 ms net after
  removing the `stripDefaultUndefined` AST walk), saving ~1.4 KB.
- On a standard `-O2` `hello_world.c` build (~8.5 KB JS), `minify_sync`
  goes from 3.2 ms to 5.5 ms (+2.3 ms).

This change, in general, should prevent us from being tempted to
replicate compression passes that already exist in upstream terser
(See emscripten-core#27871, for example).

See: emscripten-core#27839
@sbc100
sbc100 requested a review from kripken October 9, 2026 04:58
@sbc100

sbc100 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Should be good to go now.

@kripken

kripken commented Oct 9, 2026

Copy link
Copy Markdown
Member

Really? Why? Do you expect -O2 output to be readable? Remember can always add -O2 --minify=0 if you prefer.

I meant compile time, not readability. That is, I am usually ok with making -O3 slower when it helps output quality, but I think -O2 is a compromise that should be faster? In between -O1 and -O3

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this seems fast enough for -O2 as well.

@sbc100
sbc100 merged commit 30df0e8 into emscripten-core:main Oct 9, 2026
41 checks passed
@sbc100
sbc100 deleted the terser-compress branch October 9, 2026 18:18
sbc100 added a commit to sbc100/emscripten that referenced this pull request Oct 9, 2026
Enable `reduce_vars` and `unused` in Terser `--compress` (added in
emscripten-core#27926) in `tools/acorn-optimizer.mjs`.

This inlines `const` values into their expression uses and drops the
resulting unused local `const` declarations (along with unused function
parameters and single-use inner functions) in optimized builds that do
not run Closure Compiler.

This allows us to use `const` in JS library code without paying a code
size cost, while reducing total JS code size in
`test_no_closure_code_size` by 612 bytes.
sbc100 added a commit to sbc100/emscripten that referenced this pull request Oct 9, 2026
Enable `reduce_vars` and `unused` in Terser `--compress` (added in
emscripten-core#27926) in `tools/acorn-optimizer.mjs`.

This inlines `const` values into their expression uses and drops the
resulting unused local `const` declarations (along with unused function
parameters and single-use inner functions) in optimized builds that do
not run Closure Compiler.

This allows us to use `const` in JS library code without paying a code
size cost, while reducing total JS code size in
`test_no_closure_code_size` by 612 bytes.

Timing highlights (`test/hello_world.c` with `-sINCLUDE_FULL_LIBRARY`,
~247 KB minified JS):
- `minify_sync` with `compress: {defaults: false, evaluate: true,
  keep_fargs: false}`: 165.1 ms (250,514 B)
- `minify_sync` adding `reduce_vars: true, unused: true`: 224.3 ms
  (+59.2 ms, or +26.7 ms for `unused` and +28.8 ms for `reduce_vars`),
  saving ~3.2 KB (247,346 B).
- On a standard `-O2` `hello_world.c` build (~8.4 KB JS), `minify_sync`
  goes from 3.9 ms to 6.2 ms (+2.3 ms), saving 96 B.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants