The Emscripten C/asm.js local stack and the JavaScript execution call stack
are two separate entities. The Emscripten C/asm.js local stack lives in the
Emscripten HEAP and all the local variables in the C code live in that
stack. There are several ways to examine the bytes used in this stack:
  - read the STACKTOP JavaScript variable.
  - take an address of a local variable in a function (the C stack grows up
in Emscripten)
  - Use the JS Runtime.stackAlloc() function to allocate some bytes on the
C stack, and examine where those bytes were received at. This gives the
position the C stack is at.

But however, like you now explained, it doesn't look like your code runs
out of the Emscripten C stack, but the JS engine call stack. This stack is
completely shielded from manipulation in JS code for security purposes. A
hackish way to examine the call stack depth is to count how many lines
there are in the string "new Error().stack.toString()", since there's one
line per function call in there.

The number of JS local variables in a function affects the maximum depth in
terms of calls that the JS call stack can be, so if the compiled version of
the function uses up a huge number of local variables, the JS call stack
limit may be exhausted quickly.

To limit the number of local variables in a function, you can try the
following:
   - use an aggressive optimization (-O3) to remove the number of locals,
   - use the linker flag -s AGGRESSIVE_VARIABLE_ELIMINATION=1 to try to
remove the number of locals further (
https://github.com/kripken/emscripten/blob/master/src/settings.js#L190),
   - use the function outliner, e.g. -s OUTLINING_LIMIT=5000 (
https://github.com/kripken/emscripten/blob/master/src/settings.js#L169)
   - restrict LLVM use of function inlining by a) avoiding setting any
--llvm-lto if you happened to be using that, and b) setting e.g. -s
INLINING_LIMIT=50 to restrict LLVM inlining (see
https://github.com/kripken/emscripten/blob/master/src/settings.js#L169 )
   - manually break up the large function into smaller separate chunks.

If none of those help, there is of course a chance that there's a
miscompilation - looking at that function, it seems to be at least special
in that it's very large, and that it uses gotos in it, which might be a
combination that's quite rare in practice.

2015-02-13 10:34 GMT-08:00 Clifford Wolf <[email protected]>:

> Thanks for your reply,
>
> On Friday, February 13, 2015 at 6:21:48 PM UTC+1, jj wrote:
>>
>> The numbers for the available stack size that you print out look very odd
>> to me. At recursion level 4, it is stating that it has 17300 bytes(?) of
>> stack space left.
>>
>
> Not bytes. Recursion levels until "Maximum call stack size exceeded" for a
> trivial javascript function. I posted the code, here is it again:
>
> EM_ASM_({
> var i = 0;
> function stackExplorer() { i++; stackExplorer(); }
> try { stackExplorer(); } catch (e) { console.log("--> recursion level: " +
> $0 + ", free stack: " + i); }
> }, recursion_counter);
>
> Each recursion level of stackExplorer() increases the variable i until the
> call stack is exceeded and a "Maximum call stack size exceeded" exception
> is thrown. This exception is caught and "i" is printed. I don't know of any
> method to measure the available or used size on the stack in bytes. But I
> can do it this way.
>
> I have added another call like that to my main function:
>
> --> main, free stack: 20946
> --> recursion level: 1, free stack: 18221
> --> recursion level: 2, free stack: 16463
> --> recursion level: 3, free stack: 14700
> --> recursion level: 3, free stack: 14700
> --> recursion level: 2, free stack: 16463
> --> recursion level: 3, free stack: 14700
> ....
> --> recursion level: 8, free stack: 5876
> --> recursion level: 9, free stack: 4112
> --> recursion level: 9, free stack: 4112
> --> recursion level: 8, free stack: 5876
> --> recursion level: 9, free stack: 4112
> --> recursion level: 10, free stack: 2347
> --> recursion level: 11, free stack: 583
>
> /home/clifford/Work/handicraft/2014/verilearn/yosys/yosys.js:84
>       throw ex;
>             ^
> RangeError: Maximum call stack size exceeded
>
>
> So all the stuff in the stack trace I posted above the first call to
> AstNode::simplify() takes up as much space on the call stack as 2725
> recursions of stackExplorer(); lets call it Stack Explorer Units (SEU). I
> count 13 C functions in the JavaScript stack trace I posted. So that's 209
> SEU avg. per C function. This is already interestingly high. But
> for AstNode::simplify() I get 1765 SEU and thus my stack of initially 20946
> SEU is exhausted after only a few recursions.
>
>
>> Assuming that you have not changed the default Emscripten stack size
>> during compilation, which is 5MB (see here: https://github.com/
>> kripken/emscripten/blob/master/src/settings.js#L54
>> <https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fkripken%2Femscripten%2Fblob%2Fmaster%2Fsrc%2Fsettings.js%23L54&sa=D&sntz=1&usg=AFQjCNGmHlJXNBJ10_JHbPpDogNPtDhA9Q>
>> ),
>>
>
> Changing this parameter has no effect. (I have tried that already and now
> I have tried again just to be sure.)
>
> In my understanding this is the size of the data stack. But I am running
> out of space on the call stack of the JavaScript virtual machine. (I would
> not know how emscripten could have a parameter for that. To the best of my
> knowledge there is no interface to manipulate the VM call stack size from
> JavaScript..)
>
> void foo()
>> {
>>    int localTempArray[1024*1024]; // Consumes 4MB of stack space.
>>    // ...
>> }
>>
>
> But this consumes 4MB on the _data_ stack. For example:
>
> #include <emscripten.h>
>
> void test(int i, char *p)
> {
> EM_ASM_({
> var i = 0;
> function stackExplorer() { i++; stackExplorer(); }
> try { stackExplorer(); } catch (e) { console.log("--> recursion level: " +
> $0 + ", free stack: " + i); }
> }, i);
>
> char *stuff[256*1024];
> stuff[i] = p;
> test(i+1, p);
> }
>
> int main(int, char **argv)
> {
> test(1, 0);
> return 0;
> }
>
>
> Compile and run:
>
> $ emcc test.cc
> $ node a.out.js
> --> recursion level: 1, free stack: 20940
> --> recursion level: 2, free stack: 20937
> --> recursion level: 3, free stack: 20933
> --> recursion level: 4, free stack: 20930
>
> /home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:84
>       throw ex;
>             ^
> abort() at Error
>     at jsStackTrace
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:987:13)
>     at stackTrace
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:1004:22)
>     at abort
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:8711:25)
>     at __Z4testiPc [test(int, char*)]
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:5085:70)
>     at __Z4testiPc [test(int, char*)]
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:5098:2)
>     at __Z4testiPc [test(int, char*)]
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:5098:2)
>     at __Z4testiPc [test(int, char*)]
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:5098:2)
>     at __Z4testiPc [test(int, char*)]
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:5098:2)
>     at _main
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:5110:2)
>     at Object.asm._main
> (/home/clifford/Work/handicraft/2014/verilearn/yosys/a.out.js:8491:19)
>
>
> Notice how this error differs from the error I posted above?
>
> This program runs out of data stack. But on the call stack it has plenty
> of space left and only takes 3-4 SEU per recursion. My program runs out of
> call stack. But not because of the number of recursion levels but because
> of the large number of SEUs that each recursion level costs.
>
> I would like to know how I can reduce the number of SEUs that my function
> takes on the call stack (I would assume that on the JavaScript side it has
> to do with the number of local variables or something like that) and how
> this relates to the C++ code that I feed into emcc, so I know how I have to
> refactor my function to make it work.
>
> ..or if it turns to be something like the number of local variables, maybe
> there is something that emcc can do to automatically encapsulate them and
> put them on the heap or something to reduce the SEU of the generated
> function. That would be awesome.
>
> Thanks for reading all the way to the end. Help is very appreciated.
>
> regards,
>  - clifford
>
> --
> 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 [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to