-
Notifications
You must be signed in to change notification settings - Fork 3.4k
LLVM Backend
The original emscripten compiler was written in JavaScript, which was very useful for quickly prototyping new ideas during development of the various new methods needed for effective compilation to JavaScript (the relooper, longjmp tricks, C++ exceptions in JS, etc.). It is also quite stable at this point and generates very good code. However, it has a few downsides:
- Compiler speed. The generated code is fast, but generating the code is not so fast. Especially with full optimizations on, builds can be quite slow. This is not an issue for tens of thousands of lines of code, and is annoying but not horrible for hundreds of thousands, but it a serious problems for millions.
- LLVM backends integrate more closely with LLVM, and can leverage LLVM's internal code analysis and optimization. The original compiler just parses LLVM bitcode externally, so it cannot benefit from internal capabilities of LLVM.
- An upstream LLVM backend is easier to use for people than a separate project. Compiling to JS should, as much as possible, be just another backend in a compiler.
Fastcomp addresses those issues: It is a new compiler core for emscripten, written as an LLVM backend in C++.
Fastcomp is a new compiler core, replacing much of the original compiler core in src/*.js
. This replaces only the JS compiler itself, not the toolchain code nor library code (src/library*.js
) nor JS optimizer code. For context, the core compiler is a few thousand lines of code, to be replaced with a few other thousand lines of code, whereas all the other stuff not being replaced is far larger.
Fastcomp is much more streamlined than the original compiler - the original compiler supports dozens of various code generation modes (no typed arrays, typed arrays in various modes, asm.js vs non-asm.js, etc.). Fastcomp on the other hand is directly focused on asm.js code generation, which has proven to give the best results.
Fastcomp is far faster than the original compiler, often 4x or so. Perhaps more importantly, it avoids pathological cases of compiler slowness. Finally, it already emits better code in many cases, and has more potential for improvements there.
The original compiler is still present, and you may want to use it if you need a feature not present in fastcomp. There should be very few such features, as everything not deprecated or planned to be rewritten has already been ported. However, if you do need one of those features, you can use the old compiler, by building with
EMCC_FAST_COMPILER=0
in the environment. This will turn off fastcomp.
Some features not present in fastcomp are:
- Embind does not work, as it is not asm.js compatible yet, and the new backend is asm.js specific. There are plans to experiment with other approaches to binding between the languages that should eventually fix this.
- Various deprecated settings.js options (e.g. FORCE_ALIGNMENT, HEAP_INIT, etc.) have no effect. You should receive a compile-time error if you use a setting which is not yet supported, if it has not been missed.
- Linking of asm.js shared modules (note that normal static linking as used by almost all projects works fine, it is just specifically the options MAIN_MODULE and SIDE_MODULE that do not work). This is not deprecated, but may need to be partially reconsidered, so it has not been ported to fastcomp.
Fastcomp is an LLVM backend. It is not in upstream LLVM, it is far too new for that (but hopefully eventually will be). So you need to use the emscripten fork of LLVM. You can either build it from source, or get it as part of the emscripten SDK.
This means that if you use another build of LLVM - like an older one you built yourself, or one from your linux distro's repos, etc. - it will not contain fastcomp. Emscripten should then fall back to the old compiler. The downsides to you will then be
- Not taking advantage of the benefits of fastcomp (faster compilation, better generated code).
- Using the old compiler which is deprecated and consequently less tested.
To use fastcomp, you need both emscripten (see the Tutorial) and the emscripten LLVM code, either from the SDK or from source. Instructions from source are as follows:
-
git clone https://github.com/kripken/emscripten-fastcomp
, which is based off of PNaCl's LLVM fork (https://chromium.googlesource.com/native_client/pnacl-llvm). cd emscripten-fastcomp/tools/
-
git clone https://github.com/kripken/emscripten-fastcomp-clang.git clang
- note: into a dir named "clang"! This is based off PNaCl's clang fork (https://chromium.googlesource.com/native_client/pnacl-clang ). - Build it:
-
cd ..
to get back to the root of the llvm checkout -
mkdir build
and thencd build
-
../configure --enable-optimized --disable-assertions --enable-targets=host,js
(note: debug builds might not work, use only release for now, as in this build command) - (Alternatively, you can use CMake instead of configure:
cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;JSBackend" -DLLVM_INCLUDE_EXAMPLES=OFF
-DLLVM_INCLUDE_TESTS=OFF
, replace X86 if you are on something else.) -
make -j 4
(or whatever number of cores you want to use) - Set it up in ~/.emscripten (set the path to the llvm checkout +
/build/Release/bin
as LLVM_ROOT) - To use the new compiler, build with
EMCC_FAST_COMPILER=1
in the env, for example
EMCC_FAST_COMPILER=1 ./emcc -O2 tests/hello_world.cpp
Note that EMCC_FAST_COMPILER=1
will no longer be needed once fastcomp is on by default, which should happen soon (in that case, EMCC_FAST_COMPILER=0
will function to disable it).
To get a stable snapshot of emscripten development, use the master branch in both emscripten and emscripten-fastcomp. You can also use the incoming branch in both if you want the very latest changes, but those branches can sometimes have breakage. (Note that we have not modified clang, so which branch you use there does not matter.)
-
If you are building a large project, you will need a 64-bit build of llvm+clang, as compiling and optimizing can take more memory than a 32-bit build can use.
-
To build 64 bit using cmake and visual studio, use the -G "Visual Studio 10 Win64" directive. Note: VS 11/12 don't work yet.
-
If you want to build with MinGW instead and have that in path, replace -G directive in above with "-G MinGW Makefiles", and run mingw32-make to build (not tested yet).
The backend is in the repo linked to above, and code is in lib/Target/JSBackend/
. The main file is JSBackend.cpp
but the the other files in that directory are important too.
There is also the I64 simplification pass, which is currently in lib/Transforms/NaCl/ExpandI64.cpp
, but which should move to another directory probably. There are also some other passes there to lower exceptions, setjmp, etc.