Teach metadce about WASM_ESM_INTEGRATION#27218
Conversation
|
I can review the code here, but first, @brendandahl can you please confirm that this API (as in the description) is the one we want, long term? |
|
Looks as I'd expect. I believe we talked about trying to get rid of the |
| # import, so any export binaryen drops (including internal ones like the | ||
| # indirect function table) must also be dropped from the JS import to | ||
| # keep the two module interfaces in sync. | ||
| if shared.is_user_export(native_name) or settings.WASM_ESM_INTEGRATION: |
There was a problem hiding this comment.
@sbc100 do you know why we test is_user_export here? It seems like if binaryen drops an export, the JS side can't import it - nothing will arrive there - so we can remove it in non-esm mode too..?
kripken
left a comment
There was a problem hiding this comment.
Looks good aside from my comments/questions
febb4fd to
b17de01
Compare
|
I have a problem reviewing this. I see two force-pushes since my last review. The first is which shows a large diff, most of it not from this PR, so I guess there was a merge of upstream mixed in? The second is which is empty. How can I see the actual diff compared to my last review, of this PR itself? (We have had this problem in the past with force-pushes, and I'm hoping there is a better solution now? If not, avoiding force-pushes might be the only solution?) |
|
I've undone the squash here, so the additional commit after the original PR is now visible again in the commit history. |
84d9702 to
2785940
Compare
|
Hmm, the second commit has whitespace changes to Changelog.md, and also other changes I don't recognize/expect - is the content there correct? |
|
I also think I need to review the first commit again, unless there is a way to verify it is the code I've already reviewed? I don't see a way to do that but I might be missing it. (The dates are not helpful there, both from an hour ago, though I guess git dates are of limited use anyhow.) If there isn't a good solution here I can just review this entire PR's content from scratch, but that will take me longer, and in that case I'd ask to avoid force-pushes in the future. Sorry to be pedantic about this! @sbc100 has heard me complain about it many times before 😄 But I like to be able to know exactly what I've reviewed and what not. |
|
In that past I've managed to make @kripken / GitHub happy but recreating a version of the history that contains that exact original SHA that was used in the initial version of the PR and then ensuring that the rest of the change build on that. I'm not sure what happened here exactly but if you can find the original SHA that @kripken reviewed you can build a new version of the history based on that (and using merge commits to pull in changes from from main). You can then force push this new version of the history and then his "reviewed" status but will be correct again for the original chunks/files. |
2785940 to
f62f6dc
Compare
|
Apologies for the annoying limitation of Github here. I myself use rebase and squash all the time and it also often annoys @kripken .. because it destroys the Github review history (a feature I myself much use when I'm reviewing). |
kripken
left a comment
There was a problem hiding this comment.
Thanks, I managed to review this now. Final comments below, lgtm otherwise.
Building with -sWASM_ESM_INTEGRATION at -O2 or above failed in the JS
optimizer's metadce pass with 'could not find the assignment to
"wasmImports"'. In this mode the wasm<->JS boundary is expressed with
native ES import/export syntax rather than the wasmImports object and
wasmExports['x'] member uses that metadce pattern-matches.
Teach the three metadce-related acorn-optimizer passes about the ES form:
- emitDCEGraph reads 'import {..} from "./x.wasm"' as wasm export nodes
(collapsing aliases such as memory/wasmMemory to one node) and
'export { js as wasmName }' as wasm import edges, leaving re-exports
(export { _main }) to root naturally.
- applyDCEGraphRemovals prunes unused specifiers from those statements.
- applyImportAndExportNameChanges applies minified names to the
wasm-facing side of each specifier.
building.py also drops dropped exports (including internal ones like the
indirect function table) from the JS import to keep the two interfaces
in sync, and link.py keeps import/export name minification on for ESM
while disabling the now-pointless import module name minification.
1e79811 to
099f505
Compare
This makes
-sWASM_ESM_INTEGRATIONwork at-O2and above. Previously such builds failed in the JS optimizer's metadce (dead code elimination) pass with:The reason is that metadce reconstructs the wasm⇄JS def/use graph by pattern-matching the idioms a normal build emits — the
wasmImportsobject literal andwasmExports['x']member uses. Under ESM integration those don't exist: the boundary is expressed with native ES module syntax instead, which is exactly the simpler, tooling-visible def/use graph this mode was designed to produce:So rather than synthesise the old shapes, metadce now reads the real module bindings directly.
emitDCEGraphrecognisesimport {..} from './x.wasm'as wasm export nodes (collapsing aliases such asmemory/wasmMemoryonto a single node) andexport { js as wasmName }as wasm import edges, while leaving re-exports (export { _main }) to root naturally.applyDCEGraphRemovalsprunes unused specifiers from those sameimport/exportstatements.applyImportAndExportNameChangesapplies binaryen's minified names to the wasm-facing side of each specifier, e.g.becomes
The local binding is preserved (an unaliased
memorybecomesb as memory, not a brokenb).Two supporting changes keep the two module interfaces in sync:
building.py— under ESM integration any export binaryen drops (including internal ones like the indirect function table) is also dropped from the JSimport, so the JS never imports something the wasm no longer exports.link.py— import/export name minification stays on for ESM, but import module name minification is disabled, since every import is rewritten to the single support-module source bycreate_esm_wrapperanyway (and minifyingenv→awould break that rewrite).Test coverage:
js_optimizerfixtures exercising each pass on ESM-shaped input:emitDCEGraph-esm,applyDCEGraphRemovals-esm,applyImportAndExportNameChanges-esm.other.test_metadce_esm_integrationbuildinghello_world.cwith-O3 -sWASM_ESM_INTEGRATION, asserting the dangling table import is removed and names are minified consistently (the run is guarded on Node 25).Resolves #27217.
Made with AI assistance under my review