• Stars
    star
    164
  • Rank 228,730 (Top 5 %)
  • Language
    C++
  • License
    MIT License
  • Created over 2 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

C++ WebAssembly assembler in a single header file

wasmblr

A single header file WebAssembly assembler.

This library makes it easier to generate web assembly binaries directly from C++. Useful for JIT compilation from within projects compiled with Emscripten. For examples see below, or read the test.cc file.

Some benchmarks:

Contributions welcome!

Usage

#include "wasmblr.h" and compile with -std=c++11 or higher.

In C++:

struct Code : wasmblr::CodeGenerator {
  Code() : wasmblr::CodeGenerator() {
    auto add_func = function({f32, f32}, {f32}, [&]() {
      local.get(0);
      local.get(1);
      f32.add();
    });
    export_(add_func, "add");
  }
};


Code c;
auto bytes = c.emit();
std::ofstream wasm("add.wasm", std::ios::binary);
wasm.write((char*)bytes.data(), bytes.size());

If you'd prefer to avoid inheritance, you can use the code generator directly:

wasmblr::CodeGenerator cg;
auto add_func = cg.function({cg.f32, cg.f32}, {cg.f32}, [&]() {
  cg.local.get(0);
  cg.local.get(1);
  cg.f32.add();
});
cg.export_(add_func, "add");

auto bytes = cg.emit();
std::ofstream wasm("add.wasm", std::ios::binary);
wasm.write((char*)bytes.data(), bytes.size());

And then, in JavaScript:

const wasm = fs.readFileSync('add.wasm'); // or however you'd like to load it
const m = new WebAssembly.Module(wasm);
const instance = new WebAssembly.Instance(m, {});
// use the function
console.log(instance.exports.add(8, 9));

Test

With node.js installed,

g++ test.cc -std=c++11 -o test
./test

Supported Features

The semantics of the assembler attempt to mimic the WebAssembly standard closely. In the case of reserved keywords in C++ (such as export, xor, etc.), the mnemonic has an underscore appended (e.g. export_, i32.xor_).

A couple of example uses follow:

Recursion

struct Code : wasmblr::CodeGenerator {
  // NB: Needs to be a class variable; the function body is evaluated later
  uint32_t factorial;
  Code() : wasmblr::CodeGenerator() {
    factorial = function({f32}, {f32}, [&]() {
      local.get(0);
      f32.const_(1.0f);
      f32.lt();
      // base case
      if_(f32);
      {
        f32.const_(1.0f);
      }
      else_();
      {
        local.get(0);
        local.get(0);
        f32.const_(1.0f);
        f32.sub();
        call(factorial);
        f32.mul();
      }
      end();
    });
    export_(factorial, "factorial");
  }
};

Blocks

If-statements

struct Code : wasmblr::CodeGenerator {
  Code() : wasmblr::CodeGenerator() {
    auto if_func = function({f32}, {f32}, [&]() {
      f32.const_(0.0f);
      local.get(0);
      f32.gt();
      if_(f32);
      f32.const_(0.0f);
      else_();
      local.get(0);
      end();
    });
    export_(if_func, "relu");
  }
};

Loops

struct Code : wasmblr::CodeGenerator {
  Code() : wasmblr::CodeGenerator() {
    auto loop_fn = function({}, {i32}, [&]() {
      auto i = local(i32);

      loop(void_);
      {
        local.get(i);
        i32.const_(1);
        i32.add();
        local.set(i);

        local.get(i);
        i32.const_(10);
        i32.lt_s();
        br_if(0);
      }
      end();
      local.get(i);
    });
    export_(loop_fn, "loop");
  }
};

Memory

struct Code : wasmblr::CodeGenerator {
  Code() : wasmblr::CodeGenerator() {
    memory(1, 10).export_("mem");
    auto store = function({}, {}, [&]() {
      i32.const_(0);     // index 0
      i32.const_(1337);  // value 1337
      i32.store(0, 0);   // align 0, offset 0
    });
    export_(store, "store");
  }
};

SIMD (32-bit lanes for now)

struct Code : wasmblr::CodeGenerator {
  Code() : wasmblr::CodeGenerator() {
    memory(1, 10).export_("mem");
    auto square = function({}, {}, [&]() {
      auto vec = local(v128);
      i32.const_(0);
      v128.load();
      local.set(vec);

      local.get(vec);
      local.get(vec);
      v128.f32x4_mul();
      local.set(vec);

      i32.const_(0);
      local.get(vec);
      v128.store();
    });
    export_(square, "simd_square");
  }
};

TODO

Many things. I would appreciate any help filing issues for missing things!

More Repositories

1

mebm

zero-dependency browser-based video editor
JavaScript
917
star
2

jott

A simple way to jot down notes
JavaScript
111
star
3

cache.py

Python memoization across program runs.
Python
111
star
4

vim-multiuser

a multiple user vim plugin
Python
105
star
5

emojicam

personal project to render webcam data directly as emoji 😃
TypeScript
71
star
6

webpipe

A simple command line websocket utility for piping messages to and from a browser.
C
55
star
7

btb

Blue-text Bot AI. Uses Ollama + AppleScript
Python
49
star
8

pytorch_compiler_tutorial

Codebase associated with the PyTorch compiler tutorial
C++
44
star
9

mlvt

machine learning visualization tools in the terminal
Python
35
star
10

lazy

Python Library for Lazy Interfaces
Python
34
star
11

better_bindings

Better bindings for Python
Python
17
star
12

avalanche_playground

Structural (non-cryptographic) Python implementation of the Avalanche algorithm by TeamRocket
Python
15
star
13

torch_julia

[alpha] Expose Julia functions to PyTorch
C++
15
star
14

brr.js

trying to make WebGPU a bit easier to use
JavaScript
15
star
15

web_assembly_experiments

A repo to hold some simple experiments
C++
14
star
16

lofii

JavaScript
13
star
17

kayvee

model UI experiments
HTML
8
star
18

pic2emoji

convert images to emoji art
Python
7
star
19

bram.town

bram.town projects
TypeScript
5
star
20

webtorch

Python
3
star
21

template_array

Array manipulation at compile time.
C++
2
star
22

hma

lightweight differentiable multi-dimensional arrays
C++
1
star
23

evorati

Python
1
star
24

chess

C++
1
star
25

nnc_benchmark_server

Python
1
star
26

shumai_old

fast ML in JS with bun + flashlight
TypeScript
1
star
27

kq

C++
1
star
28

bison-example-calc-

hosting this -- from the bison gnu repo
C++
1
star