Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I somehow build webassembly code *without* the emscripten "glue"?

Can I somehow create a wasm file, that will work on its own as described in MDN here (by instatiating the objects and calling functions on them)?

All the guides I can find (such as this one on MDN) recommend using emscripten; that will, however, also include ~70kB "glue code" (with ~50 kB optional filesystem emulation), that has additional logic (like detection node/browser environment and automatic fetching etc), and probably some other emulation.

What if I don't want that "glue code" and want to just create WASM directly (probably from C code, but maybe something else)? Is that possible right now?

like image 786
Karel Bílek Avatar asked Jul 25 '17 06:07

Karel Bílek


2 Answers

You can use emscripten to generate fairly minimal code output.

Consider the following trivial file adder.c:

int adder (int a, int b) {     return a + b; } 

Compile it like this (requires a fairly recent emscripten):

emcc -O2 -s WASM=1 -s SIDE_MODULE=1 -o adder.wasm 

To see what it generated, disassemble it to wast textual form using binaryen's wasm-dis (you can also use wasm2wast from wabt):

wasm-dis adder.wasm -o adder.wast 

The disassembled source should look something like this:

(module  (type $0 (func (param i32 i32) (result i32)))  (type $1 (func))  (import "env" "memoryBase" (global $import$0 i32))  (import "env" "memory" (memory $0 256))  (import "env" "table" (table 0 anyfunc))  (import "env" "tableBase" (global $import$3 i32))  (global $global$0 (mut i32) (i32.const 0))  (global $global$1 (mut i32) (i32.const 0))  (export "__post_instantiate" (func $2))  (export "runPostSets" (func $1))  (export "_adder" (func $0))  (func $0 (type $0) (param $var$0 i32) (param $var$1 i32) (result i32)   (i32.add    (get_local $var$1)    (get_local $var$0)   )  )  (func $1 (type $1)   (nop)  )  (func $2 (type $1)   (block $label$0    (set_global $global$0     (get_global $import$0)    )    (set_global $global$1     (i32.add      (get_global $global$0)      (i32.const 5242880)     )    )    (call $1)   )  )  ;; custom section "dylink", size 5 ) 

You can then run this in node (v8.X or later) like this:

const WA = WebAssembly,       env = {memoryBase: 0,              tableBase: 0,              memory: new WA.Memory({initial: 256}),              table: new WA.Table({initial: 0, element: 'anyfunc'})},       code = new Uint8Array(require('fs').readFileSync('adder.wasm')) WA.compile(code).then(m => {     return new WA.Instance(m, {env: env}) }).then(i => {     console.log(i.exports._adder(7, 8)) }) 

Note that if you want to support code that uses the stack and/or heap memory things get more complicated. I.e. you'll at least need to set memoryBase and call __post_instantiate from your host environment before you call any other exports.

If you want to interpret WebAssembly code without a JavaScript environment you can run it using wac/wace (full disclosure: I created this project). Note that wace assumes you have a "_main" or "main" function defined.

like image 178
kanaka Avatar answered Sep 17 '22 20:09

kanaka


LLVM now supports direct compilation of C to wasm using WASI. Emscripten is no longer necessary.

If no libc is required, you can use LLVM right out of the box. For example, the file foo.c can be compiled with:

clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -o foo.wasm foo.c 

Otherwise, the WASI-libc project has a standalone libc that can be used.

A complete procedure for compiling C to WebAssembly with LLVM and running it in a browser is available in this post.

like image 31
Rich Apodaca Avatar answered Sep 19 '22 20:09

Rich Apodaca