facebook / hermes

A JavaScript engine optimized for running React Native.
https://hermesengine.dev/
MIT License
9.64k stars 618 forks source link

WASM support within Hermes? #429

Open paulshapiro opened 3 years ago

paulshapiro commented 3 years ago

Problem

At present it does not look like Hermes has support for running WASM via a global.WebAssembly.

Solution

Would love to see an option for doing essentially the inverse of this:

https://github.com/react-native-community/jsc-android-buildscripts/blame/4399436b9263992b16070559b81f02fbaa5af67e/scripts/compile/jsc.sh#L58

Additional Context

JS-only polyfills bringing WebAssembly support into the runtime are bound to be slow .. So I was thinking to support WASM running on Android, either a custom-build JSC (but that's not very future-proofed as we're switching to Hermes eventually), or use Hermes with some way to enable WASM, or try to find some react-native module for android to polyfill the lack of a global.WebAssembly. Seems to me that since iOS WebViews support WASM fairly well now, Android demand will be there.

This could aid porting lots of existing apps to React Native, so seems like a good idea.

farwayer commented 11 months ago

My five cents. Wasm mostly is not about performance but is about versatility and portability.

This is a universal bytecode that is already supported by many languages and interpreters, and allows you to build a binary file once and use it in many ecosystems and platforms, from the web to the Linux kernel.

As an example, the Polakdot blockchains ecosystem, in which Wasm is used everywhere. And working with it from React Native causes some difficulties.

It would be great if Wasm in Hermes would work faster than js. But even if the execution speed is at the level of similar javascript code, this will already be a big step forward.

tmikov commented 11 months ago

@farwayer you can already convert Wasm to Asm.js and run it in Hermes, if you don't care about native-like performance.

farwayer commented 11 months ago

@tmikov yes, but now it causes some difficulties:

  1. we need to recompile hermes with the HERMES_RUN_WASM flag and use this version in RN if we want intrinsics optimizations (causees error now, see https://github.com/facebook/hermes/pull/1134)
  2. we need to increase MAX_RECURSION_DEPTH for complex wasm code or we will get an error
    smoldot-asm.js:47650:514: error: Too many nested expressions/statements/declarations
    ...                                                        label$456 : {
  3. this increases the bundle size (4.7M wasm transforms to 17M hermes bytecode, with wasm2js optimizatons enabled)
  4. if wasm does not exist on its own, but as part of a library that loads it via WebAssembly (for example smoldot https://github.com/smol-dot/smoldot/blob/main/wasm-node/javascript/src/internals/local-instance.ts) then we need to rewrite or patch the library
farwayer commented 11 months ago

And the ability to develop React Native applications or parts thereof in other programming languages (for example, python) would be a great option.

To be honest, when hermes came out I was a little surprised that he used his own bytecode instead of making a fast wasm interpreter and harnessing the power of the ecosystem growing around it.

I can understand why this was done. Creating your own bytecode can allow you to achieve better performance. But as a result, Hermes found himself locked in his own room IMO.

tmikov commented 11 months ago

To be honest, when hermes came out I was a little surprised that he used his own bytecode instead of making a fast wasm interpreter and harnessing the power of the ecosystem growing around it.

I can understand why this was done. Creating your own bytecode can allow you to achieve better performance. But as a result, Hermes found himself locked in his own room IMO.

@farwayer:

  1. Wasm bytecode is unsuitable for fast interpretation. While it is possible to interpret, it is really meant as an intermediate representation before compilation.
  2. Wasm bytecode cannot express JavaScript semantics concisely, even with the recent GC proposal, because it operates on typed values.
farwayer commented 11 months ago

Ah, I thought that hermes compiler statically parses javascript and generates typed bytecode too.

And I didn’t know that wasm bytecode is so bad for interpretation. Given the ban on using JIT on iOS (is that still the case?), then there is no justification for using it for bundling the app. It would be interesting to compare, by the way, how similar code compiled in wasm and run, for example, in wasm3 loses in performance compared to hermes. But that's not the subject of this conversation, of course.

@tmikov Thank you very much for the clarification.

LinusU commented 10 months ago

@tmikov really excited that there is progress made on the WebAssembly front! I wanted to add my personal use case, which I think haven't been discussed much.

I maintain quite a few open source libraries, many targeting mainly Node.js, but most of them have support for the browser as well. I also maintain a few specifically for React Native.

I love it when I can make my libraries work on as many platforms as possible, and often put in some extra work to make that happen. Having WebAssembly support in Hermes in the future, the same future where almost all React Native projects are using Hermes (since iOS JSC seemed to have removed support for the WebAssembly global in iOS 16 😭), would mean that I could make all my libraries using WebAssembly work out of the box in Node.js, Deno, browsers, and also React Native 🎉

tmikov commented 10 months ago

@LinusU unfortunately there hasn't been progress on Wasm in Hermes since we added the unsupported extensions for optimizing some asm.js.

Frankly, Wasm is not high priority for us, because we are not a browser engine. We are an engine for native apps, and logically speaking, it is much better to compile to native code rather than to Wasm.

In any case, the consensus is that adding a Wasm interpreter or a JIT to Hermes does not fit with the goals of the project. So converting Wasm to Hermes bytecode at build time is the best solution for people unwilling to recompile their Wasm code to native. This can be combined with APIs that mimic the official Wasm browser APIs, but accept Hermes bytecode instead of Wasm bytecode. The simplest way to achieve this is by using wasm2js. This actually is something that could be provided by the community, since it requires no knowledge of Hermes internals.

A more ambitious goal would be to convert Wasm directly to Hermes IR, or directly to Hermes bytecode. The latter is an interesting possibility, since it could probably be very fast, even to the degree of making it practical at runtime.

Adding Static Hermes to the mix adds another interesting wrinkle, since it will actually make it easy to compile asm.js to native, which is a roundabout way to get to native.

rpopovici commented 9 months ago

@tmikov would static-hermes be able to compile Typescript code to WASM and run it in browser? Would that WASM bundle be more performant than pure JS running on V8?

tmikov commented 9 months ago

@rpopovici It will be possible to compile both typed and untyped JS to Wasm with Static Hermes - in fact, it already is. However running in a Wasm JIT is not really our goal, so it is not something we are actively testing and optimizing for. At this time I can't make any performance promises about it. We are really focused on compiling to native in mobile and embedded scenarios.