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.
(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.
(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
- 1,752