Stream: general

Topic: Can WASM be compiled to a standalone binary?


view this post on Zulip Ondřej Čertík (Sep 27 2023 at 13:59):

I know I can use wasmtime compile -o expr2.cwasm expr2.wasm, which will generate native machine code, however it is not a standalone binary that I can just run, it seems it still requires some kind of a runtime or other infrastructure to actually run.

How can I create a standalone native binary?

We use WASM as one of the backends in the LFortran/LPython compilers. And we wrote our own WASM->x64 binary generator that generates standalone binaries, here it is: https://github.com/lcompilers/lpython/blob/5b51c3cf8879bdd8f3c567ed38262e6b9ea55126/src/libasr/codegen/wasm_to_x64.cpp. It works great, for the subset that we currently support, and it generates a native binary that you just run.

However, instead of us maintaining such a WASM to binary compiler, is there some tool that can do this that we could just use and collaborate on?

view this post on Zulip Alex Crichton (Sep 27 2023 at 14:24):

I can at least confirm that Wasmtime doesn't support this feature. (it'd be neat to add though!) I'll let others chime in though if they know of tools to do this.

view this post on Zulip Lann Martin (Sep 27 2023 at 14:32):

It would be nice if you could deserialize a module/component by mmaping from a text section.

view this post on Zulip Joel Dice (Sep 27 2023 at 14:34):

Yeah, I was just going to suggest using include_bytes!("path/to/cwasm") in a Rust program that embeds Wasmtime. You could do that today, but an mmap capability would make it more efficient.

view this post on Zulip Joel Dice (Sep 27 2023 at 14:40):

Ideally, there would be a way to do it without requiring a Rust compiler or linker to be installed, but I don't know of any such tool. Certainly would be a fun thing to build, though.

view this post on Zulip Alex Crichton (Sep 27 2023 at 14:42):

if you want to take this to the limit I think it would be possible to create a very stripped down Wasmtime build which support purely a single instance being created. We could probably use ELF sections to reserve space for linear memory and everything. In that sense it'd be theoretically possible to have a tiny libwasmtime-exe.a (or whatever) which is linked with a native linker to the *.cwasm we produce and then there's a whole bunch of static calls between everything.

That all being said it would still require a native linker and support like wasmtime-wasi so there's still a mess of runtime bits involved, and producing this artifact would be somewhat nontrivial, so probably much better to instead have a program with wasmtime-the-crate and the cwasm already mapped in in theory

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 14:59):

For our compiler, we need to create native binaries. We are happy to create a standalone library out of our WASM->x64 backend, I think it doesn't depend on anything, which would allow us to collaborate on it with you. We still need to add Apple M1 support, right now we only support 64bit x86.

In the meantime, to get started, is there some example in Rust that uses the include_bytes!("path/to/cwasm") trick? My understanding is that it would include the native code (cwasm) and then somehow run it via the wasmtime rust library? So if I compile it with cargo/rust, it will create a standalone binary? That would indeed be a big step forward to what we need, and we might be able to take it and iterate further on it.

view this post on Zulip Alex Crichton (Sep 27 2023 at 15:06):

If you have a Rust compiler on-hand it's possible to construct a project that includes the *.cwasm, excludes Cranelift, and includes Wasmtime, and runs the wasm binary. My guess though is what you're looking for is probably a precompiled *.a which is linked with the *.cwasm or similar to create an executable, and that hasn't been done yet.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 15:58):

My guess though is what you're looking for is probably a precompiled *.a which is linked with the *.cwasm or similar to create an executable, and that hasn't been done yet.

Yes, that's what I am looking for ultimately, but because it has not been done yet, the first approach is a good start:

If you have a Rust compiler on-hand it's possible to construct a project that includes the *.cwasm, excludes Cranelift, and includes Wasmtime, and runs the wasm binary.

Is there some example that does this?

view this post on Zulip Joel Dice (Sep 27 2023 at 16:01):

I just started putting an example together. I have a meeting now, but I should be able to post a GitHub link in a couple of hours.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 16:40):

@Joel Dice awesome, thank you, much appreciated!

view this post on Zulip Chris Fallin (Sep 27 2023 at 16:44):

A thought: ELF allows for the concept of an "interpreter"; in the usual Linux use-case, a dynamically-linked binary names /lib64/ld-linux.so or whatever (the dynamic linker) as its interpreter, and the kernel leaves all the processing of the actual program ELF to that. (One can actually do /lib64/ld-linux.so my-program, i.e. invoke the dynamic linker as a command, and this will work.) Could we make the interpreter field in the .cwasm (which is actually an ELF) be wasmtime itself, and make that work somehow?

view this post on Zulip Chris Fallin (Sep 27 2023 at 16:46):

Another variant of the idea could be to have a "CLI stub" that we prepend to the cwasm, and depend on a libwasmtime.so

view this post on Zulip Alex Crichton (Sep 27 2023 at 16:49):

oh man that'd be slick if you could ./foo.cwasm

view this post on Zulip Lann Martin (Sep 27 2023 at 17:06):

I don't think you can use a binary as its own .interp... :thinking:

view this post on Zulip Lann Martin (Sep 27 2023 at 17:12):

You could point at a system e.g. /lib/wasmtime-ld.so, but if you have that control you're probably better off using binfmt_misc (on linux anyway)

view this post on Zulip Chris Fallin (Sep 27 2023 at 17:13):

ah, no, "binary as its own interpreter" isn't the suggestion, rather the .interp section in the .cwasm file

view this post on Zulip Joel Dice (Sep 27 2023 at 17:13):

@Ondřej Čertík here's the example I threw together: https://github.com/dicej/cwasm-standalone. It uses Wasmtime 13's snapshot of WASI Preview 2, based on the component model. You could alternatively do the same thing using WASI Preview 1 with a cwasm based on a core module if you preferred. Happy to answer any questions about it.

Contribute to dicej/cwasm-standalone development by creating an account on GitHub.

view this post on Zulip Lann Martin (Sep 27 2023 at 17:14):

Right but what would you put in the .cwasm's .interp for wasmtime?

view this post on Zulip Chris Fallin (Sep 27 2023 at 17:42):

@Lann Martin the interp field in the .cwasm would be /usr/bin/wasmtime or similar (perhaps libwasmtime.so if it has to be a shared object, though I think PIE executables are also technically shared objects these days)

view this post on Zulip Dan Gohman (Sep 27 2023 at 18:28):

One annoying thing about using .interp is that that would mean you don't get to use the usual ld-linux*.so, which means you can't easily dynamically link to any other libraries.

view this post on Zulip Dan Gohman (Sep 27 2023 at 18:28):

An alternative would be to just make .cwasm files depend on libwasmtime.so as a normal dynamic library.

view this post on Zulip Chris Fallin (Sep 27 2023 at 19:17):

It sounds like the two major things we'd need to do that are (i) have a main() stub that we prepend, basically @Joel Dice 's thing above; and (ii) emit PLT relocs for libcalls?

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:23):

@Joel Dice beautiful, thank you! That's exactly what I wanted. I tried it, it works. I then tried my own WASM file, so far I didn't succeed, here is exactly what I tried: https://github.com/dicej/cwasm-standalone/issues/1, if you see what I am doing wrong, let me know.

Here is what I tried: diff --git a/src/main.rs b/src/main.rs index 9f17a93..e4c0ec4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,8 @@ fn main() -> Result<()> { &unsafe { Component::deser...

view this post on Zulip Joel Dice (Sep 27 2023 at 21:28):

I assume the g2.wasm in your example is a core module rather than a component (i.e. a file conforming to https://github.com/WebAssembly/component-model)? They both use the .wasm file extension, but the latter is a superset of the former. My example involves pre-compiling a component at build time, and the runtime code expects a cwasm file containing a pre-compiled component. If it gets a core module, it won't accept it. If you change the runtime code to use the Wasmtime module API instead of the component API, you can make it run a core module instead.

Repository for design and specification of the Component Model - GitHub - WebAssembly/component-model: Repository for design and specification of the Component Model

view this post on Zulip Joel Dice (Sep 27 2023 at 21:30):

When I get a chance, I can create a branch that uses WASI Preview 1 and a core module instead of a component.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:30):

@Joel Dice here is our code: https://github.com/dicej/cwasm-standalone/issues/1#issuecomment-1738103929

Here is what I tried: diff --git a/src/main.rs b/src/main.rs index 9f17a93..e4c0ec4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,8 @@ fn main() -> Result<()> { &unsafe { Component::deser...

view this post on Zulip Joel Dice (Sep 27 2023 at 21:30):

yeah, that's a core module

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:31):

I am not well-versed in WASM, we just read the spec, produced the binary ourselves and then used wasmtime and nodejs in our tests, and we use WASI preview1. So we are using "core module" it seems. What should we be using?

view this post on Zulip Joel Dice (Sep 27 2023 at 21:32):

Core module is fine -- it's just that I've been working on WASI Preview 2 and the component model lately, so that's what I defaulted to. I'll post a link to a version that uses core modules.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:32):

Very good, thank you.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:33):

@Joel Dice we use our custom WASM backend in order to have quick compilation and runtime at https://dev.lfortran.org/ and https://dev.lpython.org/. That would not be possible with using LLVM (too big and slow).

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:34):

Separately we also want a direct x86 (and arm) binary generation, not using LLVM, for Debug builds which require very fast compilation. So we thought why not use our WASM backend and just write the WASM->binary part? So we did exactly that, so far it seems very simple and maintainable.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:40):

Are you saying that the "component" is a subset of the "core module"? So it seems we should be using a "component"? For our use case (see my last comment), our main goal is to piggyback (and be compatible) with the state-of-the-art and all the WASM tooling, that way we only have to maintain what is specific to our compilers, the rest we can maintain collaboratively with the WASM community, which seems like a win-win.

view this post on Zulip Lann Martin (Sep 27 2023 at 21:46):

Components are a superset of core modules. Some relatively easy reading here: https://github.com/WebAssembly/component-model/tree/main/design/high-level

view this post on Zulip Lann Martin (Sep 27 2023 at 21:46):

(less-easy reading elsewhere in that repo :smile:)

view this post on Zulip Joel Dice (Sep 27 2023 at 21:47):

First: yes, I've read about LFortran and LPython and I think they're super exciting projects. I'm especially eager to see SciPy, OpenBLAS, etc. supported.

The component model can be considered a superset of the core WebAssembly specification. There's a tool called wit-component (part of wasm-tools) that will automatically convert from a WASI Preview 1 core module to a WASI Preview 2 component. For the time being, it's totally fine if your toolchain only targets WASI Preview 1, since you (or your users) can always use wit-component to turn that Preview 1 module into a Preview 2 component. Eventually, you'll want to consider targetting Preview 2 and the component model directly, but it's not released yet, so no hurry :)

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:47):

Ah, so let me install wit-component, and try it!

view this post on Zulip Joel Dice (Sep 27 2023 at 21:48):

If you have Rust, you can cargo install wasm-tools

view this post on Zulip Joel Dice (Sep 27 2023 at 21:49):

When using wit-component, you'll need to specify which WASI adapter to use. You'll want to use https://github.com/bytecodealliance/wasmtime/releases/download/v13.0.0/wasi_snapshot_preview1.command.wasm in order to make the component compatible with Wasmtime 13, which is what my example above uses.

view this post on Zulip Joel Dice (Sep 27 2023 at 21:50):

Since Preview 2 hasn't stabilized yet, it's still changing from one release to the next, so the adapter needs to match the host implementation in Wasmtime.

view this post on Zulip Joel Dice (Sep 27 2023 at 21:51):

More introductory docs, for reference: https://component-model.bytecodealliance.org/

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:57):

On it!

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:57):

(Yes, we are making excellent progress with compiling SciPy, can now fully compile several of the Fortran modules via our LLVM backend, and all SciPy tests pass.)

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 21:58):

Ok, I have .cargo/bin/wasm-tools but not .cargo/bin/wit-component

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:07):

ok, figured it out: https://crates.io/crates/wit-component, wasm-tools component new core.wasm -o component.wasm

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:11):

The wit-component gives me an error message: https://github.com/dicej/cwasm-standalone/issues/1#issuecomment-1738151203

Here is what I tried: diff --git a/src/main.rs b/src/main.rs index 9f17a93..e4c0ec4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,8 @@ fn main() -> Result<()> { &unsafe { Component::deser...

view this post on Zulip Joel Dice (Sep 27 2023 at 22:15):

That's what I was saying about the adapter above. Download that wasi_snapshot_preview1.command.wasm file I linked to and pass it as an --adapter argument to wasm-tools component new.
I'm almost finished with the module version of cwasm-standalone, BTW.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:18):

The conversion worked, thanks!

view this post on Zulip Joel Dice (Sep 27 2023 at 22:27):

The modules branch uses WASI Preview 1 modules: https://github.com/dicej/cwasm-standalone/tree/modules

Contribute to dicej/cwasm-standalone development by creating an account on GitHub.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:32):

Let me try it. I can't figure out how to compile the component to cwasm: https://github.com/dicej/cwasm-standalone/issues/1#issuecomment-1738177031

Here is what I tried: diff --git a/src/main.rs b/src/main.rs index 9f17a93..e4c0ec4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,8 @@ fn main() -> Result<()> { &unsafe { Component::deser...

view this post on Zulip Joel Dice (Sep 27 2023 at 22:36):

You'll probably need a build of Wasmtime with the "component-model" feature enabled (e.g. cargo install wasmtime-cli --features component-model) in order to compile components to cwasm files.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:37):

I did.

view this post on Zulip Joel Dice (Sep 27 2023 at 22:37):

hmm, maybe the CLI doesn't support it yet

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:37):

Regarding your modules branch, here are my results: https://github.com/dicej/cwasm-standalone/issues/2

Using the modules branch, commit 59c1481. diff --git a/src/main.rs b/src/main.rs index 9f17a93..e4c0ec4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,8 @@ fn main() -> Result<()> { &unsaf...

view this post on Zulip Joel Dice (Sep 27 2023 at 22:39):

ah, that should be easy to address. An exit status of zero should just be ignored. I'll take a look

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:39):

The wasm module works in wasmtime, so it should be good.

view this post on Zulip Joel Dice (Sep 27 2023 at 22:41):

Yeah, I just pushed a change. Please try the latest on the modules branch.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:41):

checking...

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:42):

It works!!

$ cargo run --release
   Compiling cwasm-standalone v0.1.0 (/Users/ondrej/repos/cwasm-standalone)
    Finished release [optimized] target(s) in 4.27s
     Running `/Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone`
25

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:43):

Thank you so much for this @Joel Dice ! I think this is solid:

$ otool -L /Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone
/Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone:
    /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.0.0)

and

$ ll -h /Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone
-rwxr-xr-x  1 ondrej  staff   4.6M Sep 27 16:42 /Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:43):

I am on Apple M1 Max.

view this post on Zulip Joel Dice (Sep 27 2023 at 22:44):

could try using strip to get it a bit smaller, maybe

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:44):

I think this is the first time that we created a binary out of LFortran on M1 without LLVM.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:44):

And without our C backend.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:45):

After strip:

$ ll -h /Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone
-rwxr-xr-x  1 ondrej  staff   3.3M Sep 27 16:45 /Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:46):

@Joel Dice one more question --- for Debug builds that I just want as fast as possible to compile (but it's ok if it runs slower), this technology seems perfect. But for Release builds, where I want maximum performance at runtime --- can WASM, once the tooling is fully developed, match the performance of LLVM?

view this post on Zulip Joel Dice (Sep 27 2023 at 22:48):

Someone on the Cranelift team, e.g. @Chris Fallin would be a better person to ask. I imagine "it depends" :smile:

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:49):

(I guess this cwasm-standalone is being compiled via rust, so it uses LLVM. However, the main code itself is just an ARM binary that wasmtime compiled from WASM, without any LLVM involved.)

view this post on Zulip Joel Dice (Sep 27 2023 at 22:49):

right, it's the cwasm performance you care about, presumably

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:50):

For Debug build I care about the performance of generating this small /Users/ondrej/repos/cwasm-standalone/target/release/cwasm-standalone binary.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:50):

Right now it's slow, since rustc is involved, but that's only the first step (the initial prototype).

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:51):

This is nice, I think we can start testing with this at our CI.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:51):

What is wasmtime doing in this cwasm "driver"? Is it just just supplying the WASI imports?

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:52):

And providing some "main" stub?

view this post on Zulip Alex Crichton (Sep 27 2023 at 22:52):

Cranelift won't perform optimizatuons at this time such as loop unrolling, vectorization, or inlining, so it's expected that llvm binaries will generally be faster.

Currently a lot of wasms are such that they are produced by llvm and cranelift takes optimized wasm and generates good code, and there we aim to be on the same level as llvm (somewhat). Definitely cranelift aims to match the optimizing tiers of peer jits like spidermonkey and v8

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:52):

@Alex Crichton awesome. LFortran will eventually be able to produce optimized WASM binary. We are implementing unrolling, vectorization and inlining right now.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:53):

So I think with cranelift this might be quite competitive!

view this post on Zulip Chris Fallin (Sep 27 2023 at 22:53):

can WASM, once the tooling is fully developed, match the performance of LLVM?

Within some margin, yeah -- there are two separate sources of the perf gap:

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:54):

@Chris Fallin thanks! Why not optionally turn off sandboxing when performance is required?

view this post on Zulip Chris Fallin (Sep 27 2023 at 22:54):

The latter can be improved, the former will always be there as "the cost of sandboxing"; I've seen it estimated that the fundamental cost of that is something like 10-30%, as a ceiling to perf

view this post on Zulip Joel Dice (Sep 27 2023 at 22:55):

What is wasmtime doing in this cwasm "driver"? Is it just just supplying the WASI imports?

Wasmtime is setting up the WASI environment and doing (hopefully minimal) preparation of the cwasm bytes for execution, setting up linear memory, and calling into the guest. It's doing more work than a normal executable since it's sandboxing the guest, limiting its access to the host.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:55):

We are planning to have two Release modes. One ReleaseSafe, which would correspond to WASM+sandboxing. And then ReleaseFast, where all bounds checking are turned off.

view this post on Zulip Chris Fallin (Sep 27 2023 at 22:55):

@Ondřej Čertík because that... wouldn't be Wasm anymore. It's not that there are separate checks we can turn off, it's that the basic building blocks require it: for example, Wasm provides a separate heap, with offsets starting at 0, and the cost we pay is adding the base pointer. Once we do that, the bounds-checking is "free" (virtual memory)

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:55):

I see, right.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:56):

So sandboxing is in some sense fundamental to WASM.

view this post on Zulip Chris Fallin (Sep 27 2023 at 22:56):

yes, exactly

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:56):

So WASM is ideal for our ReleaseSafe mode.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:58):

Joel Dice said:

What is wasmtime doing in this cwasm "driver"? Is it just just supplying the WASI imports?

Wasmtime is setting up the WASI environment and doing (hopefully minimal) preparation of the cwasm bytes for execution, setting up linear memory, and calling into the guest. It's doing more work than a normal executable since it's sandboxing the guest, limiting its access to the host.

How hard would it be to do exactly this work, but without having to call rustc all the time? So that LFortran can do this quickly?

view this post on Zulip Joel Dice (Sep 27 2023 at 22:59):

I think that's what Alex and Chris were talking about above, e.g. adding stuff to the cwasm to make it a position-independent executable. Again, I'm probably not the one to ask, since they know more about that stuff than I.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 22:59):

We do that in fact today, via our custom WASM->x64 backend. But I am hoping to find some solution that we can be compatible with the current WASM tooling.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:00):

For example, I like that cwasm that wasmtime produces --- for example LFortran could produce it directly (for speed reasons), but we can still use wasmtime to check our work.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:01):

Then we can collaborate on the final stub, the current cwasm-standalone that you just created, but in some ways that doesn't require constant rustc recompilation.

view this post on Zulip Alex Crichton (Sep 27 2023 at 23:01):

If you're able to accept a precompiled *.a source file with source you control it should be not too hard to whip up an equivalent to what Joel has

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:02):

We currently can't accept .a yet, but we will eventually.

view this post on Zulip Alex Crichton (Sep 27 2023 at 23:02):

Where your build process would create a cwasm, postprocess it slightly, and then invoke the native linker

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:02):

For the fast compilation mode we use our own linker.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:03):

But I think this can be all solved.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:03):

Step by step. :)

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:03):

As long as we are compatible at each step.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:04):

I think we will focus on cwasm and see if we can be compatible with wasmtime, and for now we can use the rustc workaround to produce the final executable.

view this post on Zulip Alex Crichton (Sep 27 2023 at 23:04):

I'm imagining that the easiest thing would basically be to precompile what Joel already wrote where the final step is taking the *.cwasm and shoving it in at link-time somehow. Unsure how well that could fit into your linking process

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:04):

Yes, exactly.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:05):

Essentially doing the linking of Joel's code and our generated binary ourselves.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:06):

Effectively we just need to control the include_bytes macro ourselves, without invoking rustc. At the binary level once everything is compiled.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:06):

I am imagining that the wasmtime rust library just needs a pointer to it, loaded in memory?

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:07):

So if we can create some C interface that accepts this pointer, that might be all that is needed. We'll use rustc to create such a "C" library and then LFortran just links everything together, no rustc involved.

view this post on Zulip Ondřej Čertík (Sep 27 2023 at 23:07):

Do you think that's possible?

view this post on Zulip Joel Dice (Sep 27 2023 at 23:09):

What format would this C library have? A .a file? A .so file? Something else?

view this post on Zulip Joel Dice (Sep 27 2023 at 23:09):

i.e. what format would LFortran expect?

view this post on Zulip Ondřej Čertík (Sep 28 2023 at 03:43):

I think .a would be the best for static linking, .so would also work. Right now we generate a static binary out of WASM, but I think we have to extend it anyway to be able to link .a and .so libraries.

view this post on Zulip Till Schneidereit (Sep 28 2023 at 11:00):

I've seen it estimated that the fundamental cost of that is something like 10-30%, as a ceiling to perf

FWIW I think that might be too high an estimate. While I can't find a source for it rioght now, IIRC RLBox as used in Firefox has a single-digit % overhead

view this post on Zulip Ralph (Sep 28 2023 at 11:37):

Ondřej Čertík said:

So sandboxing is in some sense fundamental to WASM.

Yeah, I'd emphasize this a bit. It simply isn't WebAssembly if it doesn't have the sandbox (though there are other requirements). So you can do this! And it could use the bytecode and the tooling you've discussed. It's possible. But you couldn't portray that as being "webassembly" -- no sandbox. It's really just native code processed through the spec in some sense, or it sure seems that way.... Still might be what you want to do technically, can't be sure.

view this post on Zulip Joel Dice (Sep 28 2023 at 13:47):

Ondřej Čertík said:

I think .a would be the best for static linking, .so would also work. Right now we generate a static binary out of WASM, but I think we have to extend it anyway to be able to link .a and .so libraries.

Sure, if a .a works then we could definitely produce a libcwasm-standalone.a that provides a C function which accepts a pointer to a cwasm (plus any environment variables, directory mounts, etc.) and runs it. Should be quite easy. Does LFortran have the ability to link .a files into a final executable now, or is that something you're planning to add eventually?

view this post on Zulip Ondřej Čertík (Sep 28 2023 at 14:34):

@Joel Dice perfect, let's go the .a route. We use the system linker with our default LLVM backend, and we allow linking 3rd party libraries. Our WASM backend just produces the core module, and our WASM->x64 backend currently creates the executable directly, no linking. However, we need the ability to link 3rd party code anyway, so we'll have to implement it.

view this post on Zulip Ondřej Čertík (Sep 28 2023 at 14:40):

@Ralph we are considering at least three modes:

Right now we only have two modes, Debug and ReleaseFast, and we use LLVM for both.

WASM, with sandboxing, seems like a great fit for both Debug and ReleaseSafe modes. Based on the discussion above, it might do ok for ReleaseFast as well, but LLVM might end up being a better fit there.

view this post on Zulip Ondřej Čertík (Sep 29 2023 at 00:41):

@Joel Dice here I created a proof of concept how to create the .a library: https://github.com/dicej/cwasm-standalone/pull/3, it works!! If you have suggestions how to improve it, please let me know.

This contains all kinds of hardwired details, but it creates a standalone .a library, that I then link with a C code in a clean manner. It creates a 9MB binary and it works: $ ll -h driver -rwxr-xr...

view this post on Zulip Ondřej Čertík (Sep 29 2023 at 02:39):

Question: does it make sense for LFortran to generate .cwasm directly? Is the interface API (conventions) how to call this binary relatively stable, or does it change with every wasmtime release? Is it documented somewhere?

If it changes too much, then perhaps we should collaborate at the .wasm level, and we'll maintain our own binary generation, and we can check against the cwasm-standalone above to ensure correctness, as well as against just wasmtime guest.wasm.

view this post on Zulip bjorn3 (Sep 29 2023 at 14:04):

.cwasm only works with a single wasmtime version. It has a header with tge wasmtime version and the exact abi between compiled wasm modules and the runtime changes all the time.

view this post on Zulip Alex Crichton (Sep 29 2023 at 14:05):

Yes it would not make sense for LFortran to generate *.cwasm directly, you'll want to generate *.wasm if you can

view this post on Zulip Ondřej Čertík (Sep 29 2023 at 15:40):

@bjorn3, @Alex Crichton thank you. In that case, it seems the best way forward for us is to keep producing the .wasm files as we do today, and then we'll maintain three paths forward:

We also provide a JS file that makes exactly the same wasm binary run in a browser, which we also want.

view this post on Zulip Petr Penzin (Oct 03 2023 at 23:39):

wasm2c (WABT) can produce files that can be built standalone-ish, at least you will get a native object file, it has a runtime which you'd need to link against.

view this post on Zulip Karel Hrkal (kajacx) (Jan 02 2024 at 11:53):

I saw a video showing that wasmer can take a wasm file and make a "native" executable out of it (like an .exe) by bundling the wasmer runtime into the executable, so that you don't need to have wasmer installed to run it.

Unfortunatelly, I cannot seem to find the video anymore or find any documentation on this.


Last updated: Dec 23 2024 at 13:07 UTC