Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed assertion in asm2wasm when using Asyncify and setjmp #5383

Closed
atrosinenko opened this issue Jul 13, 2017 · 12 comments
Closed

Failed assertion in asm2wasm when using Asyncify and setjmp #5383

atrosinenko opened this issue Jul 13, 2017 · 12 comments
Labels

Comments

@atrosinenko
Copy link
Contributor

Under some conditions, compilation of code using setjmp / longjmp with Asyncify enabled causes asm2wasm to crash on failed assertion.

How to reproduce

Compile the following code

#include <setjmp.h>
#include <emscripten.h>

int main() {
  jmp_buf env;
  if(!setjmp(env)) {
    emscripten_sleep(1);
    longjmp(env, 1);
  }
  return 0;
}

with emcc test.c -o test.js -s WASM=1 -s ASYNCIFY=1 (see note about version).

Expected: compiled successfully.
Actual: compilation fails with the following error:

_setjmpTable
asm2wasm: /home/trosinenko/.emscripten_ports/binaryen/binaryen-version_34/src/asm2wasm.h:1730: wasm::Asm2WasmBuilder::processFunction(cashew::Ref)::<lambda(cashew::Ref)>: Assertion `mappedGlobals.find(name) != mappedGlobals.end() ? true : (std::cerr << name.str << '\n', false)' failed.
Traceback (most recent call last):
  File "/home/trosinenko/soft/emsdk_portable/emscripten/incoming/emcc", line 13, in <module>
    emcc.run()
  File "/home/trosinenko/soft/emsdk_portable/emscripten/incoming/emcc.py", line 1785, in run
    wasm_text_target, misc_temp_files, optimizer)
  File "/home/trosinenko/soft/emsdk_portable/emscripten/incoming/emcc.py", line 2255, in do_binaryen
    subprocess.check_call(cmd)
  File "/usr/lib/python2.7/subprocess.py", line 186, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/home/trosinenko/.emscripten_ports/binaryen/binaryen-version_34/bin/asm2wasm', 'test.temp.asm.js', '--total-memory=16777216', '--emit-potential-traps', '--mem-init=test.js.mem', '--mem-base=1024', '--wasm-only', '-o', 'test.wasm']' returned non-zero exit status -6

When compiled with EMCC_DEBUG=2 , it looks like the function _main__async_cb references _setjmpTable but does not declares it, unlike the _main function that does declare it. If I swap the longjmp and emscripten_sleep invocations, then it compiles successfully.

I use Emscripten 1.37.15 with the patch from emscripten-core/emscripten-fastcomp#167

@kripken
Copy link
Member

kripken commented Jul 13, 2017

That does look like the issue. The asyncify pass moves some code out of main, and then it can't find those local vars. And I think it's a fundamental issue, since we need those setjmp helper vars in order to handle setjmp properly. In other words, it looks like asyncify is not compatible with setjmp, if asyncify rewrites a function using setjmp.

@atrosinenko
Copy link
Contributor Author

I suppose that the stack was never really unwound at run-time in any of my __async_cb* functions. Binarien just probably turned the run-time error into compile-time one. I realize that Asyncify is not maintained anymore, but reported this for references and error message googleability. :)

@afshinm
Copy link

afshinm commented Jul 19, 2017

I have exactly the same issue during compiling my Rust binary app:

HEAPU8
Assertion failed: (mappedGlobals.find(name) != mappedGlobals.end() ? true : (std::cerr << name.str << '\n', false)), function operator(), file /Users/afshin/.emscripten_ports/binaryen/binaryen-version_34/src/asm2wasm.h, line 1730.
Traceback (most recent call last):
  File "/Users/afshin/Desktop/emscripten/emsdk-portable/emscripten/incoming/emcc", line 13, in <module>
    emcc.run()
  File "/Users/afshin/Desktop/emscripten/emsdk-portable/emscripten/incoming/emcc.py", line 1785, in run
    wasm_text_target, misc_temp_files, optimizer)
  File "/Users/afshin/Desktop/emscripten/emsdk-portable/emscripten/incoming/emcc.py", line 2255, in do_binaryen
    subprocess.check_call(cmd)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 540, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/Users/afshin/.emscripten_ports/binaryen/binaryen-version_34/bin/asm2wasm', '/Users/afshin/Projects/Github/juggernaut/example/juggernaut-demo/target/wasm32-unknown-emscripten/release/juggernaut_demo.asm.js', '--total-memory=16777216', '--emit-potential-traps', '--mem-init=/Users/afshin/Projects/Github/juggernaut/example/juggernaut-demo/target/wasm32-unknown-emscripten/release/juggernaut_demo.js.mem', '--mem-base=1024', '--wasm-only', '-o', '/Users/afshin/Projects/Github/juggernaut/example/juggernaut-demo/target/wasm32-unknown-emscripten/release/juggernaut_demo.wasm']' returned non-zero exit status -6

using emcc (Emscripten gcc/clang-like replacement) 1.37.16 (commit 7ddb47936292625f525e1cbebf9774aed9c31173)

@atrosinenko
Copy link
Contributor Author

Strictly speaking, it is probably some other codegen issue resulting in the same assertion triggered in asm2wasm: now it cannot find the HEAPU8 global variable that is very core Emscripten memory representation not particularly connected with the deprecated Asyncify.

@kripken
Copy link
Member

kripken commented Jul 19, 2017

Yeah, probably a different issue that happens to hit a similar error. @afshinm, do you have an example input showing the issue?

@afshinm
Copy link

afshinm commented Jul 19, 2017

@kripken yeah, what I'm doing is compiling this public repo: https://github.com/afshinm/juggernaut/tree/master/example/juggernaut-demo

using this command:

cargo build --target=asmjs-unknown-emscripten --release --verbose

and I noticed if I remove train function call here: https://github.com/afshinm/juggernaut/blob/master/example/juggernaut-demo/src/main.rs#L39 it can compile the program.

@kripken
Copy link
Member

kripken commented Jul 19, 2017

Can you get the input files to emcc, and the emcc command that is run? (I don't have rust installed, and I don't understand how cargo works, so it would be very hard for me to get started debugging that.)

@atrosinenko
Copy link
Contributor Author

@afshinm On Linux, in case nothing else helps you can easily monitor execve invocations in a system-wide manner using execsnoop, but note that AFAIK it loads something into the kernel and is still experimental. :) So, technically, something very bad may occur, though, personally me used it several times and it worked perfectly.

@afshinm
Copy link

afshinm commented Jul 20, 2017

I hate to say this but after upgrading Rust to 1.19, the problem is gone: https://blog.rust-lang.org/2017/07/20/Rust-1.19.html

Thanks for your reply guys and sorry for not being able to help you.

@atrosinenko
Copy link
Contributor Author

Meanwhile, when I looked at Firefox console when running code with setjmps and Asyncify, I spotted

TypeError: asm.js type error: '_setjmpTable' not found in local or asm.js module scope

When I manually added the lines

  var _setjmpTable = 0;
  var _setjmpTableSize = 0;

after

var asm = (function(global,env,buffer) {
'use asm';

It was validated successfully. I suppose that this did not interfered with actual execution of the program: if these variables were referenced without these lines before, it should cause fatal error, right? After that instead of 20s load time on every load I got 25s on the first load and 3-5s on subsequent (with the same cached compiled code). Though, compiled version executed even 1.5-2x slower. I don't know why: maybe it is because of huge code base (99Mb js file with -O3 -g2) or I somehow measured it wrong. It looks that this did not changed at least execution speed in Chrome.

Maybe this kludge can be used to enable WASM builds of code using setjmps and Asyncify, but on the other hand, it turns some usage patterns from fatal errors to subtle undefined behavior that is bad at least by default.

@kripken
Copy link
Member

kripken commented Aug 22, 2017

if these variables were referenced without these lines before, it should cause fatal error, right?

If they were read it would fail, but if written it would create a new global variable. I think that's what happens when those are missing. Then it seems to work, but of course it's wrong to put those in the global scope.

Definitely a bug that those vars aren't being emitted properly.

@stale
Copy link

stale bot commented Aug 30, 2019

This issue has been automatically marked as stale because there has been no activity in the past 2 years. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.

@stale stale bot added the wontfix label Aug 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants