WASM: Tree Shaking, Export and import name mangling#7275
Conversation
| if (usedName) { | ||
| path.node.name = usedName; | ||
| // TODO remove this when fixed in @webassemblyjs | ||
| path.node.descr.id = t.numberLiteral(+path.node.descr.id.raw); |
There was a problem hiding this comment.
@xtuc This seem to be a bug in edit. Identifiers are not accepted as id.
| if (path.node.descr.id) | ||
| path.node.descr.id = t.numberLiteral(+path.node.descr.id.raw); | ||
| if (path.node.descr.name) | ||
| path.node.descr.name = t.numberLiteral(+path.node.descr.name.raw); |
0871d77 to
4131026
Compare
| generate(module) { | ||
| const bin = module.originalSource().source(); | ||
|
|
||
| const initFuncId = t.identifier( |
There was a problem hiding this comment.
Btw we need to avoid colliding with an user's export, like __webpack_init__ is a legal export.
There was a problem hiding this comment.
I take the risk for now...
| */ | ||
| const isTableImport = moduleImport => moduleImport.descr.type === "Table"; | ||
|
|
||
| const JS_COMPAT_TYPES = new Set(["i32", "u32", "f32"]); |
There was a problem hiding this comment.
u32 shouldn't be there, I believe it's an implementation details in webassemblyjs which showed up.
Also missing f64.
| return add(bin, [func, moduleExport, funcindex, functype]); | ||
| }; | ||
|
|
||
| const getImportMangleMap = module => { |
There was a problem hiding this comment.
Could we also allow an noMangle option in the rule? Might be easier to debug.
Jessidhia
left a comment
There was a problem hiding this comment.
I guess there's no infrastructure to compile the .wasm in the test suite yet?
| return Template.asString([ | ||
| `${JSON.stringify(module.id)}: function() {`, | ||
| Template.indent([ | ||
| `return promiseResolve().then(function() { return ${promise}; }).then(function(${variable}) {`, |
There was a problem hiding this comment.
If you don't need to wait for a microtask you could just resolve the promise directly.
There was a problem hiding this comment.
It's used to delay the evaluation of the promise code, because wasm modules may not be created in the "correct" order. A microtask later all variables are filled.
But you are right we don't need the microtask delay if we would use the correct order. I'll add a TODO to topo-sort them instead.
There was a problem hiding this comment.
Ok I tried but it's impossible to sort them correctly, because of splitChunks.
webpack generates code like this to load multiple parts of a chunk group: Promise.all([__webpack_require__.e(1), __webpack_require__.e(2)]). For modules with a dependency chain of a -> b -> c and with a and c in chunk 1 and b in chunk 2, we can't solve this by sorting. So I'll stay with the microtask delay solution.
There was a problem hiding this comment.
I added a comment into the code
| `${JSON.stringify(module.id)}: function() {`, | ||
| Template.indent([ | ||
| `return Promise.resolve().then(function() { return Promise.all([${promises}]); }).then(function(array) {`, | ||
| `return promiseResolve().then(function() { return Promise.all([${promises}]); }).then(function(array) {`, |
| "// object to store loaded and loading wasm modules", | ||
| "var installedWasmModules = {};", | ||
| "", | ||
| "function promiseResolve() { return Promise.resolve(); }", |
There was a problem hiding this comment.
Why no Promise.resolve.bind(Promise)?
There was a problem hiding this comment.
I would have used var x = Promise.resolve() but the problem is that Promise may not be available on bootstrap, as the user could load the Promise polyfill in the entrypoint. This ensures that Promise is not used until import() is used.
We actually had issue reports about that...
| if (result === undefined) { | ||
| path.remove(); | ||
| } else { | ||
| path.node.module = "a"; |
|
@Kovensky
We could compile wast (text format) to wasm (binary format) files. |
mangle webpack init function store WebAssembly.Instance.exports directly connect wasm modules directly when already cached
062fe94 to
22ec604
Compare
| // TODO remove this when fixed in @webassemblyjs | ||
| path.node.descr.id = t.numberLiteral(+path.node.descr.id.raw); | ||
| } else { | ||
| path.remove(); |
There was a problem hiding this comment.
This only removes a couple of bytes from the export section. We can implement proper func elimination in the future , I'ill work on that.
There was a problem hiding this comment.
This is all work we need to do at this place. DCE will be the job of the WASM minimizer (to be defined/implemented), which will operate on asset level.
There was a problem hiding this comment.
Ok, sounds good. I have a couple of wasm optimizations that we could put there as well.
| if (usedName) { | ||
| path.node.name = usedName; | ||
| // TODO remove this when fixed in @webassemblyjs | ||
| path.node.descr.id = t.numberLiteral(+path.node.descr.id.raw); |
There was a problem hiding this comment.
Sorry but I don't understand why you are renaming the internal name here?
How are the usage detected? I'm not able to trigger the DCE.
import {test} from "./test.c.wasm";
test();
// generator.js
module.isUsed("test") // test
module.isUsed("nonexisting") // nonexistingThere was a problem hiding this comment.
You need to compile in production mode or enable optimization.usedExports.
I also don't understand why this line is needed, but it crashes without with some error about Identifiers is not allowed somewhere inside.
There was a problem hiding this comment.
Ok, I couldn't reproduce it thanks. I'll clarify that.
There was a problem hiding this comment.
Maybe it's already fixed, I'll check that.
There was a problem hiding this comment.
Nope, still broken. To reproduce:
- checkout this branch
yarn jest TestCasesProduction -t wasm --watch --no-verbose- tests are passing
- remove this line
- tests are failing
Error: C:\Users\tobia\Repos\webpack\test\cases\wasm\simple\wasm.wasm?1
Unsupported node Identifier
198 | */
199 | const rewriteExportNames = ({ ast, module }) => bin => {
> 200 | return editWithAST(ast, bin, {
201 | ModuleExport(path) {
202 | const usedName = module.isUsed(path.node.name);
203 | if (usedName) {
at assertNotIdentifierNode (node_modules/@webassemblyjs/wasm-gen/lib/encoder/index.js:39:11)
at Object.encodeModuleExport (node_modules/@webassemblyjs/wasm-gen/lib/encoder/index.js:205:3)
at encodeNode (node_modules/@webassemblyjs/wasm-gen/lib/index.js:40:22)
at applyUpdate (node_modules/@webassemblyjs/wasm-edit/lib/apply.js:42:54)
at node_modules/@webassemblyjs/wasm-edit/lib/apply.js:224:17
at Array.forEach (<anonymous>)
at applyOperations (node_modules/@webassemblyjs/wasm-edit/lib/apply.js:218:7)
at editWithAST (node_modules/@webassemblyjs/wasm-edit/lib/index.js:83:44)
at bin (lib/wasm/WebAssemblyGenerator.js:200:9)
at value (lib/wasm/WebAssemblyGenerator.js:26:19)
at value (lib/wasm/WebAssemblyGenerator.js:26:26)
at value (lib/wasm/WebAssemblyGenerator.js:26:26)
at value (lib/wasm/WebAssemblyGenerator.js:26:26)
at value (lib/wasm/WebAssemblyGenerator.js:26:26)
at WebAssemblyGenerator.generate (lib/wasm/WebAssemblyGenerator.js:369:18)
| if (path.node.descr.id) | ||
| path.node.descr.id = t.numberLiteral(+path.node.descr.id.raw); | ||
| if (path.node.descr.name) | ||
| path.node.descr.name = t.numberLiteral(+path.node.descr.name.raw); |
There was a problem hiding this comment.
i'm not sure we need to rename the path.node.descr, it's only the internal reference and uses an index anyway.
There was a problem hiding this comment.
Same as above. Try to remove these lines and run the test suite
|
Could we transform this warning into an error? That will fail for sure at runtime. |
|
For maintainers only:
|
bump webassembly and few fixes
# Conflicts: # package.json # yarn.lock
| if (result === undefined) { | ||
| path.remove(); | ||
| } else { | ||
| path.node.module = WebAssemblyUtils.MANGLED_MODULE; |
There was a problem hiding this comment.
This must be variable, because otherwise every import will be from the same module.
module = file
name = named export
There was a problem hiding this comment.
or we need to update the ImportObject in the runtime but something isn't matching correctly
Few wasm fixes
# Conflicts: # yarn.lock
bump webassemblyjs 1.5.7
|
Thank you for your pull request! The most important CI builds succeeded, we’ll review the pull request soon. |
|
🎉 |
wasm: correct initializer type for rewritten globals
What kind of change does this PR introduce?
feature + bugfix
Did you add tests for your changes?
yes
If relevant, link to documentation update:
n/a
Summary
aDoes this PR introduce a breaking change?
no
Other information