emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.81k stars 3.31k forks source link

Optimizations appear to malfunction when encountering setjmp #810

Closed VWoeltjen closed 11 years ago

VWoeltjen commented 11 years ago

First, some background: I am using XMLVM (http://xmlvm.org) to cross-compile from Java to C, and Emscripten to cross-compile from C to JavaScript. XMLVM converts Java's try-catch blocks to if(setjmp(...))-else blocks, and converts throw to longjmp(...).

When cross-compiling with Emscripten without optimizations, everything works as expected (including in cases where Java exceptions are thrown - these are handled as if by appropriate catch blocks).

However, once optimizations (-O1 or -O2) are enabled, problems emerge. These have varied depending on the specific sources being cross-compiled; results include infinite loops and crashes due the execution of unexpected code paths. Notably, this occurs without any Java exceptions being thrown (that is, before any invocations of longjmp occur)

A workaround is to replace all "setjmp(...)" calls with explicit 0 values (this turns out to be easy, as XMLVM uses an XMLVM_SETJMP(...) macro). In this case, code behaves as expected even under -O1 or -O2, so long as no Java exceptions (in C, longjmp) occur. However, this effectively upgrades all exceptions to critical failures

I have prepared a minimal test case; this is a cross-compiled minimal Java application (the original Java program does nothing but create an object; I have added a printf("Hello!") to the resulting C sources to provide some output). Due to size, I have hosted this as a git repository:

https://github.com/VWoeltjen/hello-xmlvm-emscripten.git

Note that there are two tags - "desired" (which uses setjmp) and "workaround" (which replaces all setjmps with 0, effectively disabling Java exception-handling).

There is a Makefile in the dist/ folder that is already set up to use emcc, with -O1 enabled. This will produce a file "dist/build/hello.js"; running with a JavaScript interpreter (I have been using node) should print "Hello!" to the console, but falls into an infinite loop when using -O1 on the "desired" branch. (You may need to manually clean -remove the build directory - when switching between tags, if make reports no changes)

(Apologies for the size of the test case, but I haven't been able to reproduce this in a smaller form. The cross-compiled sources above include close to 500 uses of setjmp, so it may be that the issue only occurs at a certain complexity.)

Some notes:

kripken commented 11 years ago

I can't find a straightforward way to automate the debugging of this, I'm afraid. The best course of action looks like bisecting on the uses of XMLVM_SETJMP, that is, change more and more into 0 until we find the single change that makes a difference in the output. There are two appearances in .c files, changing those makes no difference. So it looks like bisection would need to be done on XMLVM_TRY_BEGIN which uses the former macro. It appears in 43 files, 492 appearances. Which sounds like a lot, but bisection and search and replace could get us to an answer without too much pain I hope. Unless there is a better way to change how these macros are used?

waywardmonkeys commented 11 years ago

Looking at the code, this looks like it might well be the classic problem of not having the right things marked as volatile (and therefore a bug in XMLVM)?

See http://pubs.opengroup.org/onlinepubs/7908799/xsh/setjmp.html for details.

VWoeltjen commented 11 years ago

Thanks waywardmonkeys! Marking variables as volatile does indeed make the issue go away. This can be fixed on the XMLVM side.

kripken commented 11 years ago

Great, closing this.