30

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?

Karel Bílek
  • 36,467
  • 31
  • 94
  • 149
  • Possibly related question with answer - https://stackoverflow.com/questions/45146099/how-do-i-compile-a-c-file-to-webassembly – Karel Bílek Jul 25 '17 at 07:16
  • note that I asked this in 2017, and this moves very quickly. And I don't have time to re-check the answers in 2019 (or later) – Karel Bílek Oct 23 '19 at 04:29

5 Answers5

16

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.

kanaka
  • 70,845
  • 23
  • 144
  • 140
  • +1 for wac! I've been trying to build something similar using the wabt interpreter, but found it lacking for my purpose. Looks like you saved me a bunch of work :) – MB-F Jul 25 '17 at 16:04
  • I accept your version, *but* there is this issue that I don't like. Hopefully they will fix it soon! https://github.com/kripken/emscripten/issues/5419 – Karel Bílek Jul 26 '17 at 00:50
9

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.

Rich Apodaca
  • 28,316
  • 16
  • 103
  • 129
6

You can use the ONLY_MY_CODE Flag, this will generate only the wasm module with no glue.js e.g.

emcc -O1 ./src/foo.cpp -o release/foo.wasm -s WASM=1 -s ONLY_MY_CODE=1

From Settings.js https://github.com/kripken/emscripten/blob/master/src/settings.js#L583 :

var ONLY_MY_CODE = 0; // This disables linking and other causes of adding extra code
                      // automatically, and as a result, your output compiled code
                      // (in the .asm.js file, if you emit with --separate-asm) will
                      //  contain only the functions you provide.
Karel Bílek
  • 36,467
  • 31
  • 94
  • 149
Lisa Schuh
  • 69
  • 3
  • 1
    Nice. Is that different from `-s SIDE_MODULE=1`? – MB-F Aug 14 '17 at 14:13
  • https://github.com/kripken/emscripten/wiki/Linking As far as I understand both eliminate the glue code used for linking the module to system libaries etc. but `only_my_code=1` goes a step further ensuring that only your code gets translated to wasm – Lisa Schuh Aug 16 '17 at 07:04
  • It seems like the ONLY_MY_CODE flag compliments the SIDE_MODULE flag eg: emcc -O1 ./Test.cpp -o ./Test.js -s WASM=1 -s SIDE_MODULE=1 -s ONLY_MY_CODE=1 ( checking the generated Test.wast ) – noontz Aug 25 '17 at 23:12
  • This does not prevent JS file from being generated. – riv Aug 28 '18 at 10:07
  • This setting does not exist anymore https://github.com/emscripten-core/emscripten/blob/master/src/settings.js – Amin Ya Jan 13 '21 at 02:34
5

You can, and it's becoming easier over time!

If you want to avoid C++ entirely it's possible to create WebAssembly modules, for example as is done in the spec tests or the WebKit test suite.

Even with C++ you can, without Emscripten. wasm-stat.us does it for e.g. the GCC torture tests. Check out its build output, or look at its source.

For example, it'll do the following for compile / link / assemble:

# Get a .o file:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-o/20020227-1.c.o --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown-wasm -c -O2 --sysroot=src/work/wasm-install/sysroot
# Link with libc:
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld-musl/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o src/work/wasm-install/sysroot/lib/libc.a
# Or without a libc (you need to provide one somehow):
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o

# Or, if you want an assembly file instead:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-s/20020227-1.c.s --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown -S -O2 --sysroot=src/work/wasm-install/sysroot
# And get the binary file:
src/work/wasm-install/bin/wast2wasm src/work/torture-s2wasm/loop-6.c.s.wast -o src/work/torture-wast2wasm/loop-6.c.s.wast.wasm

You can even download all of the waterfall's build artifacts, including full toolchains. Just click on a green box and find the download you're looking for.

If you're feeling daring you could even write your libc in JavaScript instead of linking an existing implementation written in C.

When you say "on its own", remember than WebAssembly currently can't do anything without linking to its embedder (i.e. JavaScript). The model Emscripten follows (and I expect others to do the same) is that JavaScript is microkernel and provides syscalls.

JF Bastien
  • 6,673
  • 2
  • 26
  • 34
5

UPDATE 2020

With Emscripten you can use -o my_file.wasm

That way you will have only the WASM file and if you use the flag STANDALONE_WASM it will create the .wasm AND an OPTIONAL .js glue code if needed.

Thomas Schillaci
  • 2,348
  • 1
  • 7
  • 19
  • I would accept you if I had more time to actually verify it. Since the question is old and I don't need this anymore :( – Karel Bílek Jun 18 '20 at 12:09