Skip to content

Options for future asm.js support #8085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kripken opened this issue Feb 14, 2019 · 6 comments
Closed

Options for future asm.js support #8085

kripken opened this issue Feb 14, 2019 · 6 comments
Labels

Comments

@kripken
Copy link
Member

kripken commented Feb 14, 2019

We currently support asm.js and wasm, and intend to support both for the long term. I had some ideas about options for how exactly we do that for asm.js. Consider that

  • Browsers that validate asm.js - that is, use the asm.js type system to compile it AOT, instead of treating it like other JavaScript - get a speed benefit from that. This is basically most modern browsers, except for safari, going back a few years now.
  • Almost all browsers that validate asm.js have wasm support. And wasm is even faster.
  • If we did not emit fully valid asm.js, we could actually decrease the code size of our "asm.js" output significantly (examples below). It may also actually be faster on non-validating browsers.

In other words, we can in theory support 3 things:

  • wasm
  • asm.js with validation
  • "almost asm.js" that does not validate, but is very similar to asm.js

We currently support the first 2. Maybe supporting the first and the last would be better: smaller code for "asm.js", and anyhow almost all browsers that benefit from asm.js validation can also run wasm. In other words, asm.js with validation was a brief intermediary step - how much do we still care about it? If we do, is the speed worth the code size downsides?

Why can we make "asm.js" that is smaller and faster than validating asm.js? For example,

  • Function pointers - can use just a single table instead of a separate table per type. This saves on the size of the table (no need to be a power of 2, and there's just one) and on every indirect call, e.g. TABLE[x & 511] => TABLE[x]. This is smaller and likely faster.
  • Missing operators - asm.js lacks &&, so we end up doing stuff like x ? y : 0 instead of x && y (smaller, but need to measure if it's faster).

Thoughts?

cc'ing some people that I know use the non-wasm path: @juj @Brion

@bvibber
Copy link
Collaborator

bvibber commented Feb 14, 2019

I'd be fine with dropping validating asm.js output. Evergreen browsers grok wasm, and versions of IE and Safari tied to old operating systems don't optimize for the validating mode.

I'm actually already breaking validation in our codec modules by transforming to replace Math.imul in some hot code paths that don't need the proper overflow behavior of the polyfill. :) Would not object at all to more general benefits we can get; smaller function tables would be nice.

There might be folks with libraries who prefer to ship .js rather than .js+.wasm who would miss the performance bump on evergreen browsers, but I wouldn't be surprised to see the browser optimizations go away some day too so they should be nudged towards Wasm.

@juj
Copy link
Collaborator

juj commented Feb 14, 2019

Having a third "fake-asm.js" build mode for really old browsers is a really appealing idea. Are you referring to this being something different than the current -s ASM_JS=2 option that runs Closure on the asm.js output? In such scheme, options 2) and 3) can come from the same build even, and the builds can share the same a.js file, and only differ in their a.asm.js and a.fakeasm.js files, sharing all other code files (including a.symbols file?)

We currently support the first 2.

Hmm, this is surprising? Don't we currently have all three, with -s WASM=1, -s WASM=0 and -s WASM=0 -s ASM_JS=2 respectively?

how much do we still care about it?

I think in order to answer this, there should be an audit of Firefox, Chrome and Safari version history to figure out the version range that added first impactful asm.js optimizations, and the first version that added wasm support.

Things are also muddied a bit by wasm multithreading. In order to cut down the number of fallbacks one has to maintain, wasm singlethreading may become the uninteresting in-between, and for some users instead the only shipping configurations might be asm.js singlethreading and wasm multithreading.

We currently support the first 2. Maybe supporting the first and the last would be better

I am not sure if it is mutually exclusive to support first two, or first and last. These kinds of non-asm.js validating optimizations look like various small things, TABLE[x & 511] => TABLE[x] and x ? y : 0 -> x && y look like something that an acorn pass could do (or a direct if() check in fastcomp LLVM), as opposed to mutating fastcomp to no longer be able to produce proper asm.js?

@kripken
Copy link
Member Author

kripken commented Feb 14, 2019

Having a third "fake-asm.js" build mode for really old browsers is a really appealing idea. Are you referring to this being something different than the current -s ASM_JS=2 option that runs Closure on the asm.js output?

Yeah, something different I think. ASM_JS=2 mode is just "write almost asm instead of use asm, and allow emitting stuff like memory growth and simd which won't validate anyhow". But it doesn't take advantage of the possible optimizations there.

There is also --closure 2 mode which runs closure on all the code, including asm.js. That's also separate from this idea (orthogonal - closure 2 may still be useful in some cases).

So yeah, I wasn't accurate when I said that we support just wasm and validating asm.js. What I meant was, we've focused on optimizing wasm and validating asm.js Instead, if we replace our focus with wasm and non-validating "asm.js", I think we may gain some big benefits.

how much do we still care about (asm.js validation)?

I think in order to answer this, there should be an audit of Firefox, Chrome and Safari version history to figure out the version range that added first impactful asm.js optimizations, and the first version that added wasm support.

For chrome and firefox, auto-updating prevents older versions from being a significant part of market share. StatCounter has desktop market share details - they don't even show old enough versions of chrome from before it did asm.js validation, it gets into tiny fractions at that point. Firefox is even more so, since it started validating earlier (and Firefox LTS is also well in that range too). I'm less sure about mobile, StatCounter and others don't seem to offer a breakup into browser versions.

Safari never had asm.js validation, so any change we do here would just help it.

I am not sure if it is mutually exclusive to support first two, or first and last. These kinds of non-asm.js validating optimizations look like various small things, TABLE[x & 511] => TABLE[x] and x ? y : 0 -> x && y look like something that an acorn pass could do (or a direct if() check in fastcomp LLVM), as opposed to mutating fastcomp to no longer be able to produce proper asm.js?

Yeah, some of these things, like x && y, could be pattern matched in an acorn pass. But otherwise it's not quite that simple - we can just remove the & 511, but it's still laid out as multiple separate logical asm.js tables inside the wasm table (i.e., offsets 0-31 are for signature vii, 32-37 for iidi, etc.), which is less efficient (unnecessarily large table). To maximimize efficiency, we want to not do this at the very end.

Maybe I should mention some more thoughts I had here. One option we might consider is to implement an "almost asm.js" pipeline using the LLVM wasm backend + wasm2asm + JS optimization passes. The benefits of that would be

  • Can run the latest LLVM IR optimizations (as the upstream backend is always up to date, but we don't update fastcomp often).
  • Can run the LLVM backend optimizations, which we've never done for asm.js.
  • Can run the Binaryen optimizer, also never done for asm.js (it has passes like code-folding which do useful things that would transfer over to asm.js, e.g. that pass merges duplicate code inside functions).
  • Can run meta-dce, also never done for asm.js.
  • Can optimize wasm2asm's output in whatever way makes sense for us, starting from a good position (e.g. we have a nice flat model for function pointers, unlike how asm.js ones work), and we can do that as either Binaryen passes or acorn passes, depending on the optimization.

Other benefits aside from better generated JS code:

  • Same object files for wasm and asm.js.
  • Can optionally compile using wasm object files, for fast linking (will still do wasm2asm, but as a binaryen pass that would at least be parallelizable, unlike fastcomp).
  • We can phase out the fastcomp code eventually.

@kripken
Copy link
Member Author

kripken commented Feb 16, 2019

Oh, @dschuff reminded me that actually chrome had wasm before it had asm.js validation! (the validation was built on top of the wasm compiler code, in fact)

Given that, the only risk of asm.js validation mattering (in a place where wasm is not supported) is for Firefox and Edge. Given their market share, and where their users are (desktop for both, and Firefox has excellent auto-updating there), the risk of asm.js validation mattering is really extremely small.

@stale
Copy link

stale bot commented Feb 16, 2020

This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.

@stale stale bot added the wontfix label Feb 16, 2020
@kripken
Copy link
Member Author

kripken commented Feb 16, 2020

This has been implemented as the wasm2js path, and is stable.

@kripken kripken closed this as completed Feb 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants