Skip to content

JS fallback when Wasm is not supported#10118

Merged
juj merged 27 commits intoemscripten-core:masterfrom
juj:wasm2js_fallback
Jan 24, 2020
Merged

JS fallback when Wasm is not supported#10118
juj merged 27 commits intoemscripten-core:masterfrom
juj:wasm2js_fallback

Conversation

@juj
Copy link
Collaborator

@juj juj commented Dec 28, 2019

I am not quite sure I understood the set of flags we have now. There are

  • WASM=0/1, which enables targeting to Wasm, or disables Wasm, and targets Wasm2JS unconditionally.
  • WASM2JS=0/1, which seems to be the negation of WASM(?),
  • MAYBE_WASM2JS=0/1, which had code to conditionally enable running with Wasm2JS, if a special Module['doWasm2JS'] boolean was enabled.

However MAYBE_WASM2JS did not actually generate both JS and Wasm code, but either created Wasm if -s WASM=1 (and running as JS would fail), or would create JS if -s WASM=0, (and running as Wasm would fail)

This PR changes the meaning of MAYBE_WASM2JS to mean "run as WebAssembly if support is present, otherwise run as JS fallback".

That is, if building with emcc tests\hello_world.c -o a.html -s MAYBE_WASM2JS=1. it will generate each of a.html, a.js and a.wasm, and pick to use a.wasm if Wasm is supported, otherwise it runs the Wasm2JS code in a.js.

I think that makes MAYBE_WASM2JS more useful in practice.

Also adds WASM2JS support to MINIMAL_RUNTIME.

A future change is to make MAYBE_WASM2JS=1 imply --separate-asm. That is, if running a build that has a JS fallback enabled, the VM should only download either the JS code or the Wasm code, but not both. But that's for later.

@juj
Copy link
Collaborator Author

juj commented Dec 28, 2019

Hmmh, actually now I see WASM2JS from previous implies MAYBE_WASM2JS.

What is MAYBE_WASM2JS used for exactly? Looking at the test, it seems it enables user to be able to run maybe_wasm2js.py script on the build output, to make the build output run as JS instead of wasm? That seems like some kind of internal testing mode - why not have Emscripten directly generate wasm, js or wasm+js?

Would it be ok if I deleted current MAYBE_WASM2JS option, and instead we'd have

  • -s WASM=0: always run wasm2js,
  • -s WASM=1: always run wasm,
  • -s WASM=2: generate both wasm and wasm2js, and run wasm2js as fallback if 1) wasm is not available or 2) Module['doWasm2Js'] is selected?

That would cover all the current use cases?

@juj
Copy link
Collaborator Author

juj commented Dec 28, 2019

Another option might be that I leave MAYBE_WASM2JS functionality alone as it currently is, and just add a new option -s WASM=2 for Wasm+JS builds?

@juj
Copy link
Collaborator Author

juj commented Dec 30, 2019

Went ahead with that route from above - -s WASM=2 now does a dual Wasm+JS build and picks the one that is supported.

@tlively
Copy link
Member

tlively commented Jan 2, 2020

IIRC, @kripken added MAYBE_WASM2JS to generate JS wrapper code that is able to load either a wasm module or a JS module. This is a debugging tool for investigating miscompiles in wasm2js without changing the wrapper code.

@kripken
Copy link
Member

kripken commented Jan 6, 2020

About the flags: WASM2JS is an internal flag, not meant for users to change. The user should set WASM=0 to get non-wasm output.

The initial idea with MAYBE_WASM2JS is that you build to wasm but add that flag to add the option to manually run wasm2js later. Yes, it didn't automatically build both ways, that was left to the maybe_wasm2js.py script that the test and the settings docs refer to. But definitely it would be nice to improve that, the initial state was just to show this is practical, but not fully ergonomic yet.

@kripken
Copy link
Member

kripken commented Jan 6, 2020

This PR changes the meaning of MAYBE_WASM2JS to mean "run as WebAssembly if support is present, otherwise run as JS fallback".

I'm not opposed to that, but one major use case for that flag has been to allow debugging wasm2js easily - a single build that can be flipped between wasm and wasm2js, to see where anything diverges. I'd like to preserve that, but it's just for us devs so it's fine if it requires some fiddling. From the description it sounds like WebAssembly = null; would force the wasm2js code to be loaded?

@juj juj force-pushed the wasm2js_fallback branch 4 times, most recently from cdb8f2f to 7911c81 Compare January 13, 2020 15:00
@juj
Copy link
Collaborator Author

juj commented Jan 13, 2020

Went down the route of adding a new -s WASM=2 mode that does the dual Wasm+Wasm2JS builds. This would be good to land, although I don't want to land it yet in this form, since currently the Wasm2JS code will get embedded in the main .js file, hence when running Wasm one would download both versions. I'll work on separating the Wasm2JS code to a a.wasm.js file and download it on demand.

The existing -s MAYBE_WASM2JS option and use case was kept intact.

@kripken
Copy link
Member

kripken commented Jan 13, 2020

This would be good to land, although I don't want to land it yet in this form

Not sure if this is ready for review or not, based on that?

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

What are the use cases for this new build mode? Do we have users asking for this?

@juj juj changed the base branch from incoming to master January 14, 2020 18:57
@juj
Copy link
Collaborator Author

juj commented Jan 17, 2020

What are the use cases for this new build mode? Do we have users asking for this?

Downloading a fallback JS when Wasm is not supported. Yes, Unity.

@juj juj force-pushed the wasm2js_fallback branch from 88cd80d to 44b1ed8 Compare January 17, 2020 11:10
@sbc100
Copy link
Collaborator

sbc100 commented Jan 17, 2020

What are the use cases for this new build mode? Do we have users asking for this?

Downloading a fallback JS when Wasm is not supported. Yes, Unity.

What devices do you support that don't have Wasm support?

Also I wonder how many users needs this an whether it might be something that can instead be doing at the higher level (by the code that is load the emscripten code)? Given that is hopefully rare. We also don't really want to encourage the continued use of JS give that Wasm should universally supported these days right?

@juj
Copy link
Collaborator Author

juj commented Jan 18, 2020

What devices do you support that don't have Wasm support?

It is not just about devices, but about software.

We also don't really want to encourage the continued use of JS

Love this statement! I wish it came from the JS team at Google and I could make a poster to hang on the wall :)

Wasm should universally supported these days right?

We do not currently see this to be the case, but once our analytics shows that JS is obsolete, we will be happy to drop it.

@juj juj force-pushed the wasm2js_fallback branch from f655c0f to b33b947 Compare January 19, 2020 09:15
@juj
Copy link
Collaborator Author

juj commented Jan 19, 2020

Not sure if this is ready for review or not, based on that?

Refactored this to move the JS fallback to a separate a.wasm.js file. This is now good for review.

x.send(null);
});
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Adding -s WASM=2 support to MINIMAL_RUNTIME started to make the minimal runtime loader shell quite cluttered, so migrated this whole thing to be dynamically generated instead from python.

@sbc100
Copy link
Collaborator

sbc100 commented Jan 20, 2020

What devices do you support that don't have Wasm support?

It is not just about devices, but about software.

We also don't really want to encourage the continued use of JS

Love this statement! I wish it came from the JS team at Google and I could make a poster to hang on the wall :)

Obviously I mean we don't want to see people compilomg C++ to JS.. since wasm should be everywhere. I'm pretty sure everyone on the v8 team would agree with that :)

I'm really curious to know that browsers you are targeting that don't have wasm support.

I know of one use case which I believe is ancient version of firefox OS run an old version of spider monkey. This seems like a really bad idea from a security POV and I'm not sure we want to be encouraging such deployments.

Wasm should universally supported these days right?

We do not currently see this to be the case, but once our analytics shows that JS is obsolete, we will be happy to drop it.

Obviously JS itself will not be obsolete for browsers that lack wasm... that should be a thing of the past soon I would hope.

What do you think about my suggestion that people who want dual builds can build this at a higher level if they really need it?

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

I think it might be cleaner to have WASM_AND_JS or such as a new flag, instead of WASM=2 having a special meaning.

Overall this lgtm, but I am curious to hear your thoughts on @sbc100's suggestion to do this at a higher level. In particular as I read this I wondered if we could have fewer changes to emcc.py and have a tool that is separate, that drives two emcc.py compilations and generates the "combined" build from that?

(opts, hello_webgl2_sources, {'a.html': 1565, 'a.js': 5172, 'a.wasm': 11809}) # Compare how WebGL2 sizes stack up with WebGL 1
(opts, hello_world_sources, {'a.html': 1205, 'a.js': 484, 'a.wasm': 176}),
(opts, hello_webgl_sources, {'a.html': 1335, 'a.js': 4663, 'a.wasm': 11809}),
(opts, hello_webgl2_sources, {'a.html': 1335, 'a.js': 5172, 'a.wasm': 11809}) # Compare how WebGL2 sizes stack up with WebGL 1
Copy link
Member

Choose a reason for hiding this comment

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

what's the reason for this improvement?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With dynamic loader generation, the binary() and script() functions are only included on demand. Although the .html sizes contain unminified JS until #9990 lands, so these numbers are not very meaningful until that.

@juj
Copy link
Collaborator Author

juj commented Jan 22, 2020

I'm really curious to know that browsers you are targeting that don't have wasm support.

We target a number of Webviews and other development/deployment ecosystems that do not necessarily have the latest and greatest tech with wasm.

If you would like to really talk about this in detail, I can look at connecting you with our marketing and analytics side for more precise statistics percentages, after we have shipped upgraded dual wasm+js builds in the wild. (Currently for such ecosystems we are doing asm.js only builds without wasm support)

What do you think about my suggestion that people who want dual builds can build this at a higher level if they really need it?

drives two emcc.py compilations and generates the "combined" build from that?

Build times are a major concern, and having to run a build twice for work that is already done by the toolchain "for free" would be technically silly.

I think it might be cleaner to have WASM_AND_JS or such as a new flag, instead of WASM=2 having a special meaning.

A benefit of -s WASM=2 is that it is orthogonal to -s WASM=1 and -s WASM=0 flags. (and -s WASM=2 kind of has a mnemonic that the '2' come sfrom two builds, js + wasm).

With a -s WASM_AND_JS=1 we'd get redundancy with permutations -s WASM_AND_JS=1 -s WASM=0 and -s WASM_AND_JS=1 -s WASM=1 that are both meaningless combinations? There's already -s WASM=0/1 and -s MAYBE_WASM2JS=0/1, so adding yet another -s WASM_AND_JS=1 flag would add to the number of settings that there exist, even though Wasm+JS builds are mutually exclusive with the "only JS" and "only Wasm" setting?

@juj juj force-pushed the wasm2js_fallback branch from 27d312c to 6e7c879 Compare January 22, 2020 17:15
@sbc100
Copy link
Collaborator

sbc100 commented Jan 22, 2020

Regarding continued support for JS output: The webview/bespoke target/dev target arguments make sense to me. I guess it will take some time for my dream to come true here :)

Regarding using WASM=2 vs a whole new options: I kind of agree with both side of the case.

The existing options that take more the ON/OFF I do find very confusing. For example MAIN_MODULE=1 vs MAIN_MODULE=2 I find very non-ergonomic within the codebase and as a user. I think it would have been way nicer to have two options there: MAIN_MODULE + NO_EXPORT_ALL for example. I'm curious if either of you find these multi value options hard to work with as developers and/or users?

However, @juj points out its also annoying when one has options can can contradict each other. e.g the WASM option would make no sense in the presence of WASM_AND_JS.

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Good point about the orthogonality of the flags. So WASM=2 sounds good to me. lgtm with docs in settings.js.

@kripken
Copy link
Member

kripken commented Jan 23, 2020

@sbc100 I agree that the ergonomics are not great with flags like =2, yeah. But I do think the orthogonality argument is stronger - it has less risk of mistakes and errors.

Perhaps a more ergonomic way could be to allow string names, so WASM=0, WASM=1, WASM=both (and for main module, MAIN_MODULE=dce)? Just a thought separate from this PR.

@juj juj merged commit d2114d1 into emscripten-core:master Jan 24, 2020
@sanriqing
Copy link

sanriqing commented May 6, 2020

hi, sorry for english .
i set WASM=2
the doc writing
// Specify -s WASM=2 to target both WebAssembly and JavaScript at the same time. // In that build mode, two files a.wasm and a.wasm.js are produced, and at runtime // the WebAssembly file is loaded if browser/shell supports it. Otherwise the // .wasm.js fallback will be used. .
But I can't find the compatible code in the output code . and set window.WebAssembly = undefined at the first line . the code don't work well .
Looking forward to reply 😊

version info:

emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.39.12

@VictorQueiroz
Copy link

VictorQueiroz commented Feb 18, 2021

Can we make MAYBE_WASM2JS=1 work without replacing WebAssembly global constructor? As a way of deciding if it will use asm.js or WASM builds. Apparently, it replaces if you enable doWasm2JS option, making it inconvenient to use it in environments where WebAssembly is available.

@kripken
Copy link
Member

kripken commented Feb 18, 2021

@VictorQueiroz If someone has an idea, a PR would be welcome of course. I don't have a simple one myself atm. But this should be easy to work around, I think, if you add some scoping,

(function(WebAssembly) {
  // Put all the emscripten output here.
  // It is fine to do WebAssembly = ... here, and it will not affect the global scope.
})(WebAssembly);

That will read WebAssembly from the global scope, then send it as a parameter to the inner scope. In that inner scope it is just a local, so it can be overridden without altering the global scope. Perhaps we could do this in emscripten somehow, but I'm not sure we want to do it by default.

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.

6 participants