Managed to get valid asm.js, now getting an exception

274 views
Skip to first unread message

Marcos Scriven

unread,
Mar 17, 2013, 8:13:57 AM3/17/13
to emscripte...@googlegroups.com
Hi

Just to give a bit of background, there were two issues preventing me getting valid asm.js:

  • A '_raise' funciton, which it turned out was coming from GMP lib - that was simple to remove by switching off HAVE_RAISE macro
  • Having removed that there were a a number of validation issues (as reported here https://github.com/kripken/emscripten/issues/951). However, they all seemed to be in code that wasn't used, so 

Now that I'm getting valid asm.js, I'm getting a weird error (in FF nightly 22.0a1 (2013-03-06))

[12:01:42.344] Error: Successfully compiled asm.js code
[12:01:44.986] uncaught exception: 5640992 - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.
[12:01:44.997] The character encoding of the HTML document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the page must be declared in the document or in the transfer protocol. @ file:///Users/marcosscriven/Desktop/converter.html

So points to ask/raise:

  • Why does it say 'Error: Successfully ...' - is that just a log misclassification in FF nightly? Which was it? Successful, or error?
  • What is the exception '5640992'? That can't refer to a line number, as even un-minified my JS has 'only' 561371 lines. Obviously with asm.js I can't use the disable options anyway.
  • What's especially odd is that deliberately making the very same JS file invalid asm.js (by just adding the _raise function stub back), it will fail the asm.js validation, and work fine.
Would really appreciate some guidance here - the promise of asm.js speed up is just too good to miss out on.

Thanks again

Marcos

Marcos Scriven

unread,
Mar 17, 2013, 8:16:30 AM3/17/13
to emscripte...@googlegroups.com
Sorry, missed part of my sentence at the beginning. Should say:

     However, they all seemed to be in code that wasn't used, so I played with LLVM opts settings until dead code elimination got rid of those parts in the first place. 

Bruce Mitchener

unread,
Mar 17, 2013, 8:35:59 AM3/17/13
to emscripte...@googlegroups.com
Which LLVM pass did you use to get rid of the dead code?

I've found situations where the CFG was left in an invalid state at the end of opt -O2 or opt -O3 and an additional -simplifycfg was required to get it back into a valid state.

Invalid state in this case means that there were constructs like: "br i1 undef ..." or "resume ... undef".

I have a pull request in (but I need to gather some additional stats for Alon) at https://github.com/kripken/emscripten/pull/954 ... if you'd like to give that a try or report some performance numbers, that'd be great. I'm also trying to pursue this upstream with the LLVM folks.

 - Bruce



On Sun, Mar 17, 2013 at 7:16 PM, Marcos Scriven <mar...@scriven.org> wrote:
Sorry, missed part of my sentence at the beginning. Should say:

     However, they all seemed to be in code that wasn't used, so I played with LLVM opts settings until dead code elimination got rid of those parts in the first place. 

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Marcos Scriven

unread,
Mar 17, 2013, 9:12:24 AM3/17/13
to emscripte...@googlegroups.com
Hi Bruce

I played with LLVM opts seemingly virtually ad infinitum. Here's what I ended up with:

--llvm-opts "['-internalize', '-internalize-public-api-list=convert', '-globalopt', '-ipsccp', '-deadargelim','-simplifycfg', '-prune-eh', '-disable-inlining', '-functionattrs', '-argpromotion', '-simplify-libcalls','-jump-threading', '-simplifycfg', '-tailcallelim', '-simplifycfg', '-reassociate', '-loop-rotate','-licm', '-loop-unswitch', '-indvars', '-loop-deletion', '-loop-unroll', '-memcpyopt', '-sccp','-correlated-propagation', '-dse', '-adce', '-simplifycfg','-strip-dead-prototypes', '-globaldce', '-constmerge', '-basicaa', '-gvn', '-loop-idiom','-lower-expect','-tbaa', '-sroa']"

The reason I ended up specifying them all manually, and how I found them:

  • If I compiled with -O2 --llvm-opts 0, everything worked fine
  • Looking at Emscripten code, -O2 maps to an LLVM opt level of -O3. Compiling with that, something in there suddenly starts making an assertion fail deep within a dependent lib
  • Again in the Emscripten code (ln 986 of shared.py on incoming), the mapping from -O2 to llvm opt -O3 happens only if 'unsafe' opts aren't allowed. And 'unsafe' is true only when you are using TYPED_ARRAYS 2 (which I have to). So I manually overrode 'unsafe' to False, and my code worked a little faster, without the assertion error.
  • I then essentially 'diffed', the list of 'safe' opts chosen within shared.py, with the default passes in -O3. That yielded this list:
#llvm_opts += ['-basicaa']
#llvm_opts += ['-gvn']
#llvm_opts += ['-loop-idiom']
#llvm_opts += ['-lower-expect']
#llvm_opts += ['-tbaa'] 
#llvm_opts += ['-sroa'] # Made a huge difference. From 12 seconds to 8 on one of my tests
 
#llvm_opts += ['-instcombine'] # Causes assertion errors
#llvm_opts += ['-early-cse'] # Causes assertion errors

So, it looked like including instcombine or early-cse causes the assertion error. 

Regarding which one of those actually got rid of the offending code in terms of asm.js validation, I'd have to go back and start trying to fiddle with the options again. 

However, I do, as you can see, have the simplifycfg pass (as it was in the list of 'safe' opts chose in shared.py ln 1011).

It'll try now with and without that opt to see how it affects compilation time and, if it works, runtime speed.

Marcos


Marcos Scriven

unread,
Mar 17, 2013, 9:32:45 AM3/17/13
to emscripte...@googlegroups.com
I just tried both with and without simplifycfg pass:

With: Overall compile time 2m 16s, just the LLVM opt step, 53s
Without: Overall compile time 2:00, just the LLVM opt step, 47s

Tiny change (1Kb less with simplycfg, on a 7.4MB file)

No change in runtime performance

Seems to make no difference unfortunately to the exception I get trying asm.js in FF nightly.

Marcos

Marcos Scriven

unread,
Mar 17, 2013, 11:26:48 AM3/17/13
to emscripte...@googlegroups.com
It looks to be the -gvn LLVM opt pass that removes the 'Function-pointer table index expression needs & mask' asm.js validation errors for me.

Still haven't been able to get around the '12:01:44.986] uncaught exception: 5640992 - Exception catching is disabled, this exception cannot be caught. ' error though unfortunately.

Alon Zakai

unread,
Mar 17, 2013, 7:31:05 PM3/17/13
to emscripte...@googlegroups.com
On Sun, Mar 17, 2013 at 5:13 AM, Marcos Scriven <mar...@scriven.org> wrote:
> Hi
>
> Just to give a bit of background, there were two issues preventing me
> getting valid asm.js:
>
> A '_raise' funciton, which it turned out was coming from GMP lib - that was
> simple to remove by switching off HAVE_RAISE macro
> Having removed that there were a a number of validation issues (as reported
> here https://github.com/kripken/emscripten/issues/951). However, they all
> seemed to be in code that wasn't used, so
>
>
> Now that I'm getting valid asm.js, I'm getting a weird error (in FF nightly
> 22.0a1 (2013-03-06)):
>
>>> [12:01:42.344] Error: Successfully compiled asm.js code
>>>
>>> [12:01:44.986] uncaught exception: 5640992 - Exception catching is
>>> disabled, this exception cannot be caught. Compile with -s
>>> DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.
>>>
>>> [12:01:44.997] The character encoding of the HTML document was not
>>> declared. The document will render with garbled text in some browser
>>> configurations if the document contains characters from outside the US-ASCII
>>> range. The character encoding of the page must be declared in the document
>>> or in the transfer protocol. @
>>> file:///Users/marcosscriven/Desktop/converter.html
>
>
> So points to ask/raise:
>
> Why does it say 'Error: Successfully ...' - is that just a log
> misclassification in FF nightly? Which was it? Successful, or error?

That should be a warning (it should probably not be even that, but
just a log message). How are you viewing that? Web console or error
console?

> What is the exception '5640992'? That can't refer to a line number, as even
> un-minified my JS has 'only' 561371 lines. Obviously with asm.js I can't use
> the disable options anyway.

It's the address in memory of the C++ exception being thrown. Not very
useful... But you can print out a stack trace from where it is thrown
(search for 'throw'), which could help in a -g (debug info) build.

> What's especially odd is that deliberately making the very same JS file
> invalid asm.js (by just adding the _raise function stub back), it will fail
> the asm.js validation, and work fine.
>

You can prevent asm.js optimizations by just removing the "use asm"
string. Do you get different results when you do that? If so it's a
bug, please file it with your codebase and steps to reproduce.

- azakai

Marcos Scriven

unread,
Mar 18, 2013, 7:38:16 AM3/18/13
to emscripte...@googlegroups.com
Hi 

Thanks for the suggestion - I did a debug build, and added 'new Error().stack' to the throw function, and got this:

[11:32:13.516] Error: Successfully compiled asm.js code
[11:32:15.507] uncaught exception: 5640992 - This exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.___cxa_throw@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:10872
__ATINIT__<.func@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:852
callRuntimeCallbacks@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:695
initRuntime@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:703
@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:570109

That line pertains to (in bold):
 
// === Body ===
assert(STATICTOP == STACK_MAX); assert(STACK_MAX == TOTAL_STACK);
STATICTOP += 395056;
assert(STATICTOP < TOTAL_MEMORY);
var _stdout;
var _stdin;
var _stderr;
__ATINIT__ = __ATINIT__.concat([
  { func: function() { __GLOBAL__I_a() } },
  { func: function() { __GLOBAL__I_a486() } },

I also tried your suggestion and simply tried commenting out 'use asm;', and the issue exception no longer occurs.

BTW I'm looking at these messages in the web console (opt-cmd-k on mac)

Thanks again for all your help.

Marcos

Marcos Scriven

unread,
Mar 18, 2013, 11:10:42 AM3/18/13
to emscripte...@googlegroups.com
I'm not sure this was the best way to do it, but I just spent some time doing the following:

  • Starting with the initial stack trace, find the function it came from
  • Then iterate - look through that function for other calls, and add a return (or return 0|0) just after it, and see if the exception still happens
  • If it doesn't, then move on to the next function call within the function
  • This is somewhat time consuming, but eventually you find the next level in the trace (manually)

About six steps in the manually discovered trace, I found the error was coming from this line:

HEAP32[$this + 32 >> 2] = __Znaj($__n) | 0;

c++filt tells me that __Znaj is operator new[](unsigned int)

I added some console logs to get the values at that point:

[14:49:57.734] $__n is:4096
[14:49:57.734] $this is:5630404
[14:49:57.734] HEAP32.length:16777216

 That then calls __Znwj  which is operator new(unsigned int) - the value of size is still 4096 at that point:

function __Znwj($size) {
  $size = $size | 0;
  var $3 = 0, $5 = 0, $8 = 0, $17 = 0, $_lcssa = 0, label = 0;
  if (($size | 0) == 0) {
    $3 = 1;
  } else {
    $3 = $size;
  }
  while (1) {
    $5 = _malloc($3) | 0;
    if (($5 | 0) != 0) {
      $_lcssa = $5;
      label = 14772;
      break;
    }
    $8 = __ZSt15get_new_handlerv() | 0;
    if (($8 | 0) == 0) {
      break;
    }
    FUNCTION_TABLE_v[$8 & 4095]();
  }
  if ((label | 0) == 14772) {
    return $_lcssa | 0;
  }
  $17 = ___cxa_allocate_exception(4) | 0;
  __ZNSt9bad_allocC2Ev($17);
  ___cxa_throw($17 | 0, 5617820, 2764);
  return 0;
}

Doing the same trick of putting in returns before and after function calls there, it looks like __ZSt15get_new_handlerv or std::get_new_handler()  which for me is:

function __ZSt15get_new_handlerv() {
  return (tempValue = HEAP32[1409265] | 0, HEAP32[1409265] = tempValue + 0, tempValue) | 0;
}

So presumably that's returning 0, breaking out of the loop, and throwing an exception.

Hopefully that's of some use.

Marcos



Marcos Scriven

unread,
Mar 18, 2013, 11:25:46 AM3/18/13
to emscripte...@googlegroups.com
I think this comes from ln 143 of libcxx/new.cpp:

new_handler
get_new_handler() _NOEXCEPT
{
    return __sync_fetch_and_add(&__new_handler, (new_handler)0);
}

Which an internet search tells me is a GNU builtin?

Marcos

Bruce Mitchener

unread,
Mar 18, 2013, 11:44:15 AM3/18/13
to emscripte...@googlegroups.com
Just compile a small snippet that uses it to LLVM and it'll be more clear. If you use EMCC_DEBUG=2 and check /tmp/emscripten_temp, you'll be able to see both the LLVM and the JS.

I've got an update to a current version of libcxx pending and I'm pretty sure that the new_handler stuff all works there. (I've fixed over 200 test failures from the libcxx test suite and a lot of the remaining failures are things that emscripten doesn't support.)

 - Bruce




Marcos

--

Marcos Scriven

unread,
Mar 18, 2013, 11:51:57 AM3/18/13
to emscripte...@googlegroups.com
I shall try that - the issue was finding where it was failing in asm.js, when it doesn't fail otherwise.

I did find the difference:

function __Znwj($size) {
  $size = $size | 0;
  var $3 = 0, $5 = 0, $8 = 0, $17 = 0, $_lcssa = 0, label = 0;
  if (($size | 0) == 0) {
    $3 = 1;
  } else {
    $3 = $size;
  }
  while (1) {
    $5 = _malloc($3) | 0;
    if (($5 | 0) != 0) {
      $_lcssa = $5;
      label = 14772;
      break;    <----- When commenting out 'use asm' in my JS file, it gets here, and breaks out
    }
    $8 = __ZSt15get_new_handlerv() | 0;    <---- When in asm.js mode, it gets here, receives 0 from the new_handlerv() function, and throws an exception at the end

Alon Zakai

unread,
Mar 18, 2013, 11:38:24 PM3/18/13
to emscripte...@googlegroups.com
On Mon, Mar 18, 2013 at 4:38 AM, Marcos Scriven <mar...@scriven.org> wrote:
> Hi
>
> Thanks for the suggestion - I did a debug build, and added 'new
> Error().stack' to the throw function, and got this:
>
> [11:32:13.516] Error: Successfully compiled asm.js code
> [11:32:15.507] uncaught exception: 5640992 - This exception catching is
> disabled, this exception cannot be caught. Compile with -s
> DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to
> catch.___cxa_throw@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:10872
> __ATINIT__<.func@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:852
> callRuntimeCallbacks@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:695
> initRuntime@file:///Users/marcosscriven/Desktop/asmbuilddebug.js:703
> @file:///Users/marcosscriven/Desktop/asmbuilddebug.js:570109
>
> That line pertains to (in bold):
>
>
> // === Body ===
> assert(STATICTOP == STACK_MAX); assert(STACK_MAX == TOTAL_STACK);
> STATICTOP += 395056;
> assert(STATICTOP < TOTAL_MEMORY);
> var _stdout;
> var _stdin;
> var _stderr;
> __ATINIT__ = __ATINIT__.concat([
> { func: function() { __GLOBAL__I_a() } },
> { func: function() { __GLOBAL__I_a486() } },
>
>
> I also tried your suggestion and simply tried commenting out 'use asm;', and
> the issue exception no longer occurs.

If that is the case, then you can stop right there - it looks like a
bug in the JS engine. Please submit an issue with the testcase.

- azakai
Reply all
Reply to author
Forward
0 new messages