rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.37k stars 12.72k forks source link

Tracking issue for wasm support #38805

Closed brson closed 6 years ago

brson commented 7 years ago

It's been a while since I've looked at the state of wasm in Rust, and there's a lot more to be done for it to be really usable. Is anybody doing anything significant with Rust and emscripten yet?

Today I've triaged all the emscripten issues and put together a little roadmap. I'm hoping others can help fill in the gaps here and get this across the finish line.

I'll start by outlining the near-term scope, some discussion of the state of things, then the tasks.

Goal: Make Rust on wasm + emscripten a reliable, 1st class Rust target.

Success metrics:

Out of scope:

Discussion

Today I read reports of problems compiling from Windows and macOS; some important crates that should obviously work just don't, like regex; I have no idea whether the test suite still passes.

Setting up emcc is not foolproof, but I don't think we should worry about solving it now, since no other cross target solves the problem of acquiring the linker. Perhaps something like the cross tool will suffice - it or something like it will be needed for testing emcc targets anyway.

The issue of LLVM coupling remains the worst, and with the pending llvm upgrade it's a pressing matter. To recap, today rustc uses the emcc 'fastcomp' LLVM backend to communicate LLVM IR to emcc, to compile to wasm/asm.js. This forces rustc and emcc to share an LLVM fork, which is not sustainable (emcc LLVM development is basically complete - the only reason for them to upgrade LLVM is because we upgrade LLVM).

The only viable solution I see is to migrate to the upstream LLVM wasm backend, changing the rustc wasm model such that rustc produces wasm directly, and emcc links that. And this model unfortunately precludes asm.js support until somebody writes a wasm->asm.js translator. I think this is just the way it has to be. The future is wasm, and we can't maintain LLVM in sync with emcc for long.

I don't think though we can do the migration to the LLVM wasm backend before the next LLVM upgrade though, because that backend is not ready. So we're probably going to be asking @azakai to upgrade emcc to LLVM 4.0 and continuing with the LLVM IR -> emcc -> wasm path for a while.

Tasks

kripken commented 7 years ago

Out of scope: [..] Retaining asm.js compatibility

Just curious, why? Doing asm.js first, which is very stable, is the more reliable path, instead of going straight to wasm. It's true it requires the fastcomp update, which might or might not be a lot of work, which I don't think we know yet.

est31 commented 7 years ago

@kripken have you read further below? It seems that @brson wants to use LLVM's wasm backend, which would apparently mean that asm.js compat is dropped. It would make sense as then emcc wouldn't block the LLVM upgrade any more.

kripken commented 7 years ago

Yeah, that's a bunch of additional questions, though, as currently it is not yet stable and produces larger/slower code. But it does have the advantage of not maintaining an LLVM fork, which is considerable of course.

In principle, if we want to do the least work for rust-on-the-web, we could wait for the wasm backend or use it with the downsides, if we don't mind them.

chicoxyzzy commented 7 years ago

I'm not sure if this is a good place for my comment but I don't know where to create an issue. Please point me to the right repo in case it's off topic.

I was following instructions from https://users.rust-lang.org/t/compiling-to-the-web-with-rust-and-emscripten/7627 and had no luck in generating working example.

So I'm not sure if Rust code cross-compiles to correct wasm module at all for now (using rustc 1.14.0 and emcc 1.37.0 / 1.37.1). Am I doing something wrong?

kripken commented 7 years ago

In general you can open an issue on the binaryen tracker (often it's the right place, if not that's fine too). Regarding your issues:

chicoxyzzy commented 7 years ago

Just created https://github.com/WebAssembly/binaryen/issues/867

chicoxyzzy commented 7 years ago

Update: cross-compiling to wasm works as expected

CryZe commented 7 years ago

There's one massive problem when compiling Rust to WASM which made it fail in about half the Advent of Code puzzles I tried (and a lot of other projects including my current large project). It's the fact that Rust generates auto vectorized code which then fails in the asm2wasm conversion step as it doesn't support vectorization yet. It is impossible to turn off auto vectorization at the moment as it seems. Neither the emscripten flags nor the Rust flags seem to affect it. So this definitely needs to be fixed.

Here's the binaryen issue for it: https://github.com/WebAssembly/binaryen/issues/855

Update: Playing around more with this, this seems to just randomly work now if you pass the no vectorization flags?!

Update 2: Nvm, it still doesn't work in my larger project. This is just super odd. I guess a lot of commonly used crates do manual vectorization which you can't just turn off with flags?!

Update 3: Turns out it's not any crates that cause this. This is definitely either rustc or emcc generating vector instructions that you can't turn off.

aidanhs commented 7 years ago

@kripken

as currently it is not yet stable and produces larger/slower code

I assume the llvm parts are mostly similar, so you're referring to the emscripten asm.js optimizer passes? Which is a partially solved problem if documentation includes the three commands necessary to use ayzim (though this would require the converter to not 'finalize' the asm.js).

kripken commented 7 years ago

@aidanhs Yes, LLVM IR optimizations are identical - the difference is only in "backend" opts. But the asm.js optimizer isn't necessarily used in this case. On the LLVM backend side, LLVM does the backend opts; on the asm2wasm side, backend opts may be done by either the asm.js optimizer (from emscripten, or in principle could be ayzim) or wasm optimizer (from binaryen), or just with the wasm optimizer by itself. The last option is currently enabled by default, as it has some benefits, although the asm.js optimizer is automatically run if the code is also meant to be runnable as asm.js as an option. (I have a blogpost in the works about this stuff.)

aidanhs commented 7 years ago

Right, I see the issue more clearly now - binaryen is good, but the llvm wasm backend doesn't contain all of binaryen.

If a wasm file generated by the llvm backend were run through the binaryen optimizer, would it 'converge' to be similar to a file generated via the asm2wasm route? If so, it might be reasonable to offer the binaryen optimizer as a separate tool (or ayzim, when I port it). Depending on what the rust team prefer this optimizer (C++ or rust) could be inside rustc as a strange post-compilation pass, with the promise that the code will disappear as the llvm wasm backend gets more mature.

Regarding stability, I'd be in favour of the wasm llvm backend regardless of optimizer thoughts - more exposure will make the backend better, and the rust users giving it a go as a first class target will help make sure that it's robust come wasm 'release'.

kripken commented 7 years ago

If a wasm file generated by the llvm backend were run through the binaryen optimizer, would it 'converge' to be similar to a file generated via the asm2wasm route?

Not currently. The wasm backend doesn't emit ifs/if_elses, for example, and binaryen doesn't have passes to convert code to that form (because it doesn't need to currently, the asm.js backend uses the relooper to do that). Also the binaryen optimizer would need to undo a bunch of things the wasm backend does, like register allocation. In principle all that could be done, but it hasn't been a priority.

brson commented 7 years ago

Just curious, why? Doing asm.js first, which is very stable, is the more reliable path, instead of going straight to wasm. It's true it requires the fastcomp update, which might or might not be a lot of work, which I don't think we know yet.

@kripken the reason to move quickly to the LLVM wasm backend and concede asm.js support is that the alternative is to continue upgrading fastcomp to newer LLVM revisions, which I know is a great deal of work (edit: oops I didn't mean to contradict you - maybe it's not a lot of work). I'm happy to keep using the model we have now, running through fastcomp, for as long as it is advantageous, but the tradeoff is significant.

Looks like @dylanmckay has started the fastcomp upgrade. Thank you so much @dylanmckay ✨

brson commented 7 years ago

The C linkage issue is progressing https://github.com/rust-lang/rust/issues/39171.

Added to the op a new issue to make sure the extensions we're using are correct for all output types.

Fishrock123 commented 7 years ago

@brson The following issues appear to have been closed, think you could update their status in the OP?

Fishrock123 commented 7 years ago

Also, are there any plans/ideas to support WebAssembly standalone (Side Modules)? (https://github.com/kripken/emscripten/wiki/WebAssembly-Standalone)

CryZe commented 7 years ago

"emscripten support broken by cargo file layout changes" is still an open issue. You have a link / name mismatch there.

2017-03-14 22:48 GMT+01:00 Jeremiah Senkpiel notifications@github.com:

Also, are there any plans/ideas to support WebAssembly standalone (Side Modules)? (https://github.com/kripken/emscripten/wiki/WebAssembly- Standalone)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/38805#issuecomment-286571499, or mute the thread https://github.com/notifications/unsubscribe-auth/ABYmbpOar6sMga7nYN-iCEW6cLu9Xxa-ks5rlwsdgaJpZM4LaCnI .

eholk commented 7 years ago

@Fishrock123 WebAssembly standalone is something I think would be great to see. It's one of the things I'm trying to experiment a little with mir2wasm.

Fishrock123 commented 7 years ago

@CryZe Oops! Must have copied that wrong, fixed. :)

@eholk Right, saw that repo. Got at least some Rust running yesterday without the emscripten wrapper. :D

Eh2406 commented 7 years ago

What Is this up to? How does the update to LLVM 4.0 change things?

vadimcn commented 7 years ago

I gave it a try a little while back: upgraded LLVM to the latest master, tweaked wasm target definition, etc.

For a simple self-contained program (factorial), rustc was able to produce correct-looking wasm code. Back then, LLVM used ELF as a container for said code, but I found a tool to convert it to a straight wasm module. So far so good... However, in order to interact with the world, you need a runtime. I wanted to re-use emscripten's runtime, but at the time (and this is still true, I think), emcc did not support taking wasm modules as input, so there was no way to link it.

I've heard that LLVM recently had grown support of direct emission of wasm modules, as well as adding wasm linking in lld, so I intend to give it a second try one of these days.

rschulman commented 7 years ago

Is wasm support broken in the latest versions? I've got rustc 1.18.0-nightly (3b5754e5c 2017-04-10) and emcc (Emscripten gcc/clang-like replacement) 1.37.12 (commit ccaf4e74fa9abf51cff8d1d4823f0b4d84bf3eab) and doing a cargo build --target=wasm32-unknown-emscripten on a generic "Hello, world" is giving me a standalone javascript file in the output directly, but no .wasm file to be found anywhere. I'm totally open to the idea that I'm missing something obvious, but I have no idea what it could be at this point.

CryZe commented 7 years ago

It was broken in these versions but it has been fixed for a few weeks now. Try updating your compiler.

Ross Schulman notifications@github.com schrieb am Mo. 8. Mai 2017 um 04:32:

Is wasm support broken in the latest versions? I've got rustc 1.18.0-nightly (3b5754e5c 2017-04-10) and emcc (Emscripten gcc/clang-like replacement) 1.37.12 (commit ccaf4e74fa9abf51cff8d1d4823f0b4d84bf3eab) and doing a cargo build --target=wasm32-unknown-emscripten on a generic "Hello, world" is giving me a standalone javascript file in the output directly, but no .wasm file to be found anywhere. I'm totally open to the idea that I'm missing something obvious, but I have no idea what it could be at this point.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/38805#issuecomment-299758444, or mute the thread https://github.com/notifications/unsubscribe-auth/ABYmbqnzoNmTBJVWXkPZsB4oZeIItKYpks5r3n6egaJpZM4LaCnI .

lilianmoraru commented 7 years ago

@kripken

as currently it is not yet stable and produces larger/slower code

Wasm seems to perform better in this demo/benchmark(I'd say, very well - since it reaches almost the performance of the native application): https://davidmcneil.github.io/the-rusty-web/#benchmarks

kripken commented 7 years ago

@lilianmoraru I'm not sure, but I think that benchmark was built using asm2wasm, not the wasm backend, which is what I was referring to in that quote. What I mean is that there are two separate ways to emit wasm, asm2wasm and the wasm backend, and only the wasm backend is not yet stable or optimized for size or speed. But emitting wasm using asm2wasm is considered stable and should be faster and smaller than asm.js (and close to native).

lilianmoraru commented 7 years ago

@kripken It was built using this command: cargo build --release --target wasm32-unknown-emscripten. Reference here: https://github.com/davidMcneil/the-rusty-web#running-on-the-web

Btw, a note on the performance, it is "finicky": https://github.com/davidMcneil/the-rusty-web/issues/3

lilianmoraru commented 7 years ago

@brson If you think it is related, here is another issue I just opened for Wasm32: #42518 . Brotli has great compression rate/decompression speed ratio(https://quixdb.github.io/squash-benchmark/), so I wanted to see if I can make Dropbox's Brotli decompressor compile for Wasm32. Thinking of something like docs.rs where it could compress very well the documentation(a lot of text) once and then decompress it with Brotli on the user's side, with the help of Wasm32.

Herschel commented 7 years ago

I'm having the same problem as @rschulman, where no .wasm file is output when using cargo build --target=wasm32-unknown-emscripten. Building using rustc outputs the file. I'm on darwin nightly rustc 1.21.0-nightly (7ac979d8c 2017-08-16) and emcc 1.37.18.

alexcrichton commented 6 years ago

I'm going to close this as lots of progress has happened in the meantime and the best metabug-like-tracker for this is now currently at https://github.com/rust-lang-nursery/rust-wasm