Emscripten

for fun and profit!

Did I forgot to record my screen ?

About Me

g

F

My Fat Ass

Mud

The Plan

  • What's Emscripten
  • How it works
  • How to use it for glory and profit!

So... What's Emspriten?

And why it's so magical? 

It's a C/C++ compiler

that targets the Web!!!

c -> Emscripten -> javaScript

[here be unicorns]

[here be more unicorns]

Hello World

vitali@Moo:[~/demo]  >> gcc

Hello World

vitali@Moo:[~/demo]  >> gcc  hello.c -o hello.js

Hello World

vitali@Moo:[~/demo]  >> gcc  hello.c -o hello.js -Wall

Hello World

vitali@Moo:[~/demo]  >> emcc hello.c -o hello.js -Wall

Hello World

vitali@Moo:[~/demo]  >> emcc hello.c -o hello.js -Wall
vitali@Moo:[~/demo]  >> 

OMG! Look! it's the same interface as gcc. 

with additional settings & options  

(more on that later...)

Hello World

vitali@Moo:[~/demo]  >> emcc hello.c -o hello.js -Wall
vitali@Moo:[~/demo]  >> node hello.js 
Hello World 

OMG! Look! it's the same interface as gcc. 

with additional settings & options  

(more on that later...)

Used For Real Stuff Too!

So... How Does It Work?

bytecode

LLVM

asm.js

Time Hacking

Don't Panic!

It's Hacking Time!

source code (.c ) 

 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

compiler

> gcc hello.c -c
 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

??

compiler

> gcc hello.c -c
 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

 object file (.o) 

compiler

> gcc hello.c -c
 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

 object file (.o) 

compiler

> gcc hello.c -c
 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

 object file (.o) 

compiler

linker

> gcc hello.c -c
 > ld hello.o -o hello -lc -e main
 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

??

 object file (.o) 

compiler

linker

> gcc hello.c -c
 > ld hello.o -o hello -lc -e main
 > gcc hello.c -o hello

30 seconds about building C code...

source code (.c ) 

Profit!

 object file (.o) 

compiler

linker

> gcc hello.c -c
 > ld hello.o -o hello -lc -e main
 > gcc hello.c -o hello

30 seconds about building C code...

Note That:

ld is NOT coupled with GCC 

Note That:

ld is NOT coupled with GCC 

> man ld  | grep "include gcc" -c

0

Note That:

ld is NOT coupled with GCC 

> man ld  | grep "include gcc" -c

0

Q.E.D

But GCC itlsef is mostly monolithic...  

But GCC itlsef is mostly monolithic...  

That's what LVVM is all about!

LLVM

Backend

Optimizer

Frontend

IR

IR

LLVM

Backend

Optimizer

Frontend

IR

IR

LLVM IR

define i32 @add1(i32 %a, i32 %b) {
entry:
  %tmp1 = add i32 %a, %b
  ret i32 %tmp1
}

define i32 @add2(i32 %a, i32 %b) {
entry:
  %tmp1 = icmp eq i32 %a, 0
  br i1 %tmp1, label %done, label %recurse

recurse:
  %tmp2 = sub i32 %a, 1
  %tmp3 = add i32 %b, 1
  %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
  ret i32 %tmp4

done:
  ret i32 %b
}

LLVM

Backend

Optimizer

Frontend

IR

IR

LLVM

Backend

Optimizer

Frontend

IR

IR

Frontend

Frontend

IR

IR

Backend

Backend

IR

IR

LLVM

MIPS

Optimizer

C/C++

IR

IR

D

Haskell

IR

IR

x86

ARM

IR

IR

Emscripten is an LVVM backend

Emscripten is an LVVM backend

A Large Part Of

Emscripten is an LVVM backend

A Large Part Of

Emscripten

asm.js

LLVM Optimizer

C/C++

LLVM IR

Clang

JS Backend

LLVM IR

bytecode

LLVM

asm.js

Time Hacking

bytecode

LLVM

asm.js

Time Hacking

bytecode

LLVM

asm.js

Time Hacking

bytecode

LLVM

asm.js

Time Hacking

asm.js in 180 sec

asm.js is 100% JavaScript

asm.js is 100% JavaScript

Weird JavaScript

asm.js is 100% JavaScript

Weird JavaScript

Not very Readable By Humans  

asm.js is 100% JavaScript

Weird JavaScript

Not very Readable By Humans  

Just Like....

asm.js is 100% JavaScript

Weird JavaScript

Not very Readable By Humans  

angular.js !!

asm.js is 100% JavaScript

Weird JavaScript

Not very Readable By Humans  

angular.js !!

nah asm.js is actually useful!

C Code


int fib(int num)
{
  if (num < 1)  return 0;
  if (num == 1) return 1;

  return fib(num-1) + fib(num-2);
}

asm.js Code

function(global, env, heap) {
  'use asm';

 function _fib($num) {
  $num = $num|0;

  if (($num|0) <  (1)) return 0;
  if (($num|0) == (1)) return 1;

  return ((_fib((($num-1)|0))|0) + (_fib((($num-2)|0))|0)) |0;
 }

 return {fib : _fib};
}

asm.js Code

function($global, $env, $scope) {
  'use asm';

 function _ng$fib($num) {
  $num = $num|0;

  if (($num|0) <  (1)) return 0;
  if (($num|0) == (1)) return 1;

  return ((_ng$fib((($num-1)|0))|0) + (_ng$fib((($num-2)|0))|0)) |0;
 }

 return {FactoryServiceInjectorProvider : _ng$fib};
}

Just Sayin....

asm.js Code

function(global, env, heap) {
  'use asm';

 function _fib($num) {
  $num = $num|0;

  if (($num|0) <  (1)) return 0;
  if (($num|0) == (1)) return 1;

  return ((_fib((($num-1)|0))|0) + (_fib((($num-2)|0))|0)) |0;
 }

 return {fib : _fib};
}

asm.js Code

A Subset of JavaScript

Just like "strict mode"

function(global, env, heap) {
  'use asm';
.....

asm.js Code

No Closures 

Shared TypedArray & Globals only

function(global, env, heap) {.....

return {fib : _fib};

asm.js Code

Only primitive types

 (no strings, no objects etc...)

function _fib($num) {
  $num = $num|0;

Weird type annotations everywhere!!!!

asm.js Code

function(global, env, heap) {
  'use asm';

 function _fib($num) {
  $num = $num|0;

  if (($num|0) <  (1)) return 0;
  if (($num|0) == (1)) return 1;

  return ((_fib((($num-1)|0))|0) + (_fib((($num-2)|0))|0)) |0;
 }

 return {fib : _fib};
}

asm.js Code

That means no GC!

BTW

asm.js

=

bytecode embeded inside javascript

bytecode

LLVM

asm.js

Time Hacking

bytecode

LLVM

asm.js

Time Hacking

bytecode

LLVM

asm.js

Time Hacking

Let's build shit!

Let's assume you have this C interface:





void init(DrawDabFunctionCallback  draw_dab_cb, 
          GetColorFunctionCallback get_color_cb);


void stroke_to(float x, float y, float pressure, 
               float xtilt, float ytilt, double dtime);

Function Pointers in C Are Fun!

typedef void (*GetColorFunctionCallback) (
	float x, float y, float radius,
	float* color_r, float* color_g,
	float* color_b, float* color_a
);

typedef int (*DrawDabFunctionCallback) (
	float x, float y,
	float radius,
	float color_r, float color_g, float color_b,
	float opaque, float hardness,
	float alpha_eraser,
	float aspect_ratio, float angle,
	float lock_alpha,
	float colorize
);

Bindings!

ccall() & cwrap()

// cwrap
var init = Module.cwrap("init", "void", ["number", "number"]);
var strokeTo = Module.cwrap("stroke_to", "void", R.repeat("number", 5));


// ccall
Module.ccall("init", "void", ["number", "number"], [drawDabProxyPtr, colorProxyPtr]);
Module.ccall("stroke_to", "void", R.repeat("number", 5), R.repeat(0, 5));
-s EXPORTED_FUNCTIONS="['_init','_stroke_to']"

Passing JS Callbacks



var colorProxyPtr = Module.Runtime.addFunction(getColor);
var drawDabProxyPtr = Module.Runtime.addFunction(drawDab);
-s RESERVED_FUNCTION_POINTERS=2

C Pointers in JS

var getColor = function(x, y, radius, r_ptr, g_ptr, b_ptr, a_ptr) {
    
    var convert = R.map(R.divide(R.__, 255)); // look ma, I'm using ramda!
    var color = convert([255, 215, 0, 255]);

    Module.setValue(r_ptr, color[0], "float");
    Module.setValue(g_ptr, color[1], "float");
    Module.setValue(b_ptr, color[2], "float");
    Module.setValue(a_ptr, color[3], "float");
};
window.Module
window.Module

OMG! 

How can I prevent it from shitting all over my global scope?!

window.Module

OMG! 

Modules


var m1 = MyModule();
var m2 = MyModule();

m1.ccall(...);
m1.Runtime.addFunction(...);

m2.ccall(...);
m2.Runtime.addFunction(...);
-s MODULARIZE=1 -s EXPORT_NAME="MyModule"

Bugs ?!

Debugging

-g[4-1]

The Complete Build

emcc src/native/main.c -I src/native/libmypaint -o bin/lib.debug.js -O0 

-s EXPORTED_FUNCTIONS="['_init', '_stroke_to']" 
-s RESERVED_FUNCTION_POINTERS="2"
-s NO_EXIT_RUNTIME="1"  # don't exit after  main() is done
-s MODULARIZE="1" 
-s NO_FILESYSTEM="1" 
-s NO_BROWSER="1"

Profit!

Profit!

File Size

vitali@Moo:[/dist] (master) >> wc -l `find ../native -name '*.c'`
  .......
  5363 total

vitali@Moo:[/dist] (master) >> ls -lh

total 692K
-rw-rw-r-- 1 vitali vitali 523K libmypaint.debug.js
-rw-rw-r-- 1 vitali vitali 166K  libmypaint.release.js

File Size

vitali@Moo:[/dist] (master) >> wc -l `find ../native -name '*.c'`
  .......
  5363 total

vitali@Moo:[/dist] (master) >> ls -lh

total 692K
-rw-rw-r-- 1 vitali vitali 523K libmypaint.debug.js
-rw-rw-r-- 1 vitali vitali 166K  libmypaint.release.js

Not Bad!

File Size

vitali@Moo:[/dist] (master) >> wc -l `find ../native -name '*.c'`
  .......
  5363 total

vitali@Moo:[/dist] (master) >> ls -lh

total 692K
-rw-rw-r-- 1 vitali vitali 523K libmypaint.debug.js
-rw-rw-r-- 1 vitali vitali 166K  libmypaint.release.js

Not Bad!

-o3 -s NO_FILESYSTEM="1" -s NO_BROWSER="1"

Limitations?

sleep(10)

sleep(10)

function _usleep(useconds) {
      .....
      // We're single-threaded, so use a busy loop. Super-ugly.
      ....
        while (self['performance']['now']() - start < msec) {
          // Do nothing.
        }
     ....
    }		

sleep(10)

function _usleep(useconds) {
      .....
      // We're single-threaded, so use a busy loop. Super-ugly.
      ....
        while (self['performance']['now']() - start < msec) {
          // Do nothing.
        }
     ....
    }		

But this will improve...

"Emterpreter"

"Emterpreter"

compiles asm.js into a binary bytecode,

"Emterpreter"

compiles asm.js into a binary bytecode,

and generates an interpreter for that bytecode.

"Emterpreter"

compiles asm.js into a binary bytecode,

and generates an interpreter for that bytecode.

"Emterpreter"

compiles asm.js into a binary bytecode,

and generates an interpreter for that bytecode.

void emscripten_sleep(unsigned int ms);
void emscripten_sleep_with_yield(unsigned int ms);

OR...

OR...

Play nice in your main_loop()

OR...

Play nice in your main_loop()

int main() {
...
#ifdef __EMSCRIPTEN__
  // 60 fps , 1 -> simulate infinite loop
  emscripten_set_main_loop(one_iter, 60, 1); 
....
}

-pthreads ? 

-pthreads ? 

It's complicated...

-pthreads ? 

It's complicated...

-pthreads ? 

It's complicated...

key question: Can we have shared mutable data between Workers?

undifined behavior 

undifined behavior 

Not that bad...

undifined behavior 

Not that bad...

But sometimes you will tweak the C code

undifined behavior 

Not that bad...

But sometimes you will tweak the C code

Remember: It's still cross compilation...

Like the old Java Joke:

Like the old Java Joke:

Write once, Run everywhere!

Like the old Java Joke:

Write once, Run everywhere!

Like the old Java Joke:

Write once, DEBUG everywhere!

Other Cool Stuff

(Do We Have Time?)

Cool Stuff

Virtual Filesystem - fopen(), fread() etc..

OpenGL to WebGL bridge

SDL implementation with Canvas 

DOM from C/C++

Questions?

Emscripten

By Vitali Perchonok

Emscripten

  • 824
Loading comments...

More from Vitali Perchonok