CodeGuidlinesAndLimitations - imvu/emscripten GitHub Wiki
Emscripten can work with pretty much any C/C++ code. However, it might be useful to know what types of code Emscripten compiles better than others, if you are rewriting part of the code you will be compiling.
Code that CANNOT be compiled
These issues are fundamental difficulties. We can get around them by emulation in theory, but it would be very slow.
- Code that is multithreaded and uses shared state. JS has threads - web workers - but they cannot share state, instead they pass messages.
- Nonportable code that relies on endianness is problematic. It can work with USE_TYPED_ARRAYS=2, but it will not be portable!
- Nonportable code that relies on x86 alignment behavior. X86 allows unaligned reads and writes (so you can read a 16-bit value from a non-even address, i.e., which is only 8-bit aligned address, for example), but other archs do not: ARM will raise SIGILL, and when emscripten generates JS you will get undefined behavior in this case. (If you build your code with
SAFE_HEAP=1
then you will get a clear runtime exception, see Debugging. Note: There is also a code generation mode, UNALIGNED_MEMORY, which can support unaligned code like this - but it is very slow.) - Nonportable code that uses low-level features of the native environment, like native stack manipulation (e.g. in conjunction with setjmp/longjmp. We support normal setjmp/longjmp, but not with stack replacing etc.).
- Nonstandard struct packing (using things like
#pragma pack
) might work, but have not really been tested. We know that running a fuzzer that uses them can find problems.
Code that DOES compile, but might be slower than expected
None of the issues here is a showstopper, but they might be slower than you expect. You probably don't need to worry about this stuff, but it might be useful to know about it.
- 64-bit ints. Bitwise operations on these are reasonable, but math (+, -, *, /) is emulated, very slowly. JavaScript does not have native 64-bit ints so this is unavoidable.
- 32-bit multiplication that needs all 64 bits of precision must be emulated like 64-bit integer math (JS doubles work fine up to ~52 bits). By default precise 32-bit multiplication is off as it makes a lot of common 32-bit code very slow, but if you need it you can enable it with
-s PRECISE_I32_MUL=1
, seesrc/settings.js
for more. - 32-bit floats will end up as 64-bit doubles in JS engines. That means they might be a little slower than expected, depending on your CPU.
- memcpy/memset works, but JS engines don't optimize it very well. Native code will use SIMD on this, JS engines will not - yet.
- Structures/classes. Unlike simple variables, they are not yet nativized in Emscripten, which adds some overhead. Inner loops that do property accesses, for example, might be rewritten to avoid this.
- Exceptions. In JS such code generally makes the JS engine turn off various optimizations. For that reason exceptions are turned off by default in
-O1
and above (to re-enable them, run emcc with-s DISABLE_EXCEPTION_CATCHING=0
).
Code that MIGHT be useful to avoid
Avoiding these kinds of code might be nice to do, to prevent warnings and to enable additional optimizations. But unless it is very easy for you to avoid them, just ignore this section.
- Unions. These will trigger SAFE_HEAP warnings (like valgrind), and make the QUANTUM_SIZE=1 optimization impossible.
- Bitfields. Also SAFE_HEAP warnings.
- Reusing memory, that is, using the same allocated block of memory once for one kind of object, later for another. This prevents the QUANTUM_SIZE=1 optimization.