Skip to content

Highly experimental LLVM IR → ASM.JS compiler

License

Notifications You must be signed in to change notification settings

mnesvold/rooibos

Repository files navigation

Rooibos

Highly experimental LLVM IR → ASM.JS compiler

Rooibos converts LLVM bitcode (most commonly produced with clang -c -emit-llvm ...) into ASM.JS code¹. In theory, this means that any language that has an LLVM-compatible frontend (including C and dialects, thanks to Clang) can be used to write a web app frontend. (In practice, there's the small question of porting runtime libraries, but that's another matter.)

¹ Technically, Rooibos generates a SpiderMonkey-compatible JSON representation of the AST for ASM.JS-compliant code. You'll need another tool to actually generate the JavaScript; see below.

Installation

Roobius has some dependencies that you'll need to wire up before you can compile it.

  1. Make sure you have Tup and npm installed.
  2. Clone this repository into some directory $ROOT.
  3. mkdir $ROOT/tools; cd $ROOT/tools
  4. Somewhere, probably out of the Rooibos directory, build LLVM using the instructions here. Link these LLVM directories into your new tools directory so Rooibos can find them:
  5. ln -s where-you-want-to-build-llvm/bin ./bin — built binaries
  6. ln -s where-you-want-to-build-llvm/lib ./lib — built libraries
  7. ln -s where-you-want-to-build-llvm/include ./include-build — build-specific headers
  8. ln -s where-you-want-llvm-to-live/include ./include-src — build-independent headers
  9. Clone Niels Lohmann's JSON library: cd $ROOT/tools; git clone https://github.com/nlohmann/json.git
  10. Install escodegen and UglifyJS: cd $ROOT/tools; npm install escodegen ugilfy-js
  11. Pick a variant: tup variant configs/{default,release}.conf
  • default includes debug symbols and produces some extra test output files (human-readable .ll and minified JS) to aid development
  • release runs optimization passes on Rooibos itself and skips the extra test output
  1. Run Tup: cd $ROOT; tup

If all dependencies are in place, this will build Rooibos and run the integration tests by running Rooibos against the C files in tests and diffing against their expected results. If Tup comes back green and Updated, you're good to go.

Usage

Rooibos picks up from LLVM bitcode (.bc files). There's a working example in tests/Tupfile, but the gist of it is:

$ generate foo.bc
$ $BUILD/rooibos/rooibos-codegen < foo.bc > foo.ast
$ $TOOLS/node_modules/.bin/esgenerate foo.ast > foo.js

If you're starting from C(++) sources, the command you want is clang -c -emit-llvm -o foo.bc foo.c.

(For curiosity's sake, the Tupfile also minifies the resulting JavaScript into a separate .min.js file if you're building the default variant.)

Using the results

The produced ASM.JS function is itself wrapped in a non-ASM.JS adaptor, which is responsible for setting up the heap, bridging native JavaScript and ASM.JS types, and so on. Currently, all functions defined in the IR are exposed through the adaptor which is attached to the global variable (window in browsers) under the name ASM. For instance, a source file

int echo(int value)
{
  return value;
}

can be used like this:

console.log(ASM.echo(42));

Design goals

Rooibos is meant to be not overly verbose in its output, partially inspired by C++'s "don't pay for what you don't use" philosophy. If no source code references the heap, for instance, then the runtime won't contain the code to set up the heap in the first place. As another example, branch instructions are implemented by emulating a program counter with a local PC variable and a switch nested in a while (1) loop. Functions with only one basic block don't need to emulate the program counter, so they don't.

Implementation status

  • IR support
    • Functions
    • Global variables
    • Parameters
    • Return values
    • Calls to other ASM functions
    • Calls to the ASM.JS stdlib
    • Calls to the ASM.JS foreign function interface
    • Heap access
    • asm support
    • All other IR instructions (see README.instructions.md)
  • Type support
    • Integers
    • Floats
    • Doubles
  • Dependency system integrations
    • Direct global assignment (window.ASM = ...)
    • CommonJS (require([], function() { ... }))
    • Browserify (module.exports = ...)
  • JS integrations
    • Manipulating JS objects from ASM.JS code (via FFI)
    • Converting JS strings to const char *s and vice-versa
  • Tool support
    • Rooibos driver
    • Source maps
    • C headers for browser environment
    • C++ runtime(?)

About

Highly experimental LLVM IR → ASM.JS compiler

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published