Summary: | [WebAssembly] iOS 13 RangeError: Maximum call stack size exceeded. | ||
---|---|---|---|
Product: | WebKit | Reporter: | Kenneth Pouncey <kepounce> |
Component: | WebAssembly | Assignee: | Nobody <webkit-unassigned> |
Status: | RESOLVED FIXED | ||
Severity: | Blocker | CC: | anandtapangupta68, apple-rth, fpizlo, joe.fradley, keith_miller, kg, lewing, mark.lam, saam, webkit-bug-importer, ysuzuki |
Priority: | P2 | Keywords: | InRadar |
Version: | Safari 13 | ||
Hardware: | iPhone / iPad | ||
OS: | iOS 13 | ||
See Also: | https://bugs.webkit.org/show_bug.cgi?id=201026 |
Description
Kenneth Pouncey
2019-08-21 22:39:21 PDT
The issue exists on desktop Safari 13 as well. I created a simple example code where I am doing recursion upto 1000 levels and I am getting the `Maximum call stack size exceeded` error at about 670 recursion depth. So, it seems some limit has been set on the max stack size for WASM. All browsers have stack depth limits. Is your bug report just that our limit has become smaller? I will agree that `All browsers have stack depth limits.` It is not only that it has become smaller but applications that worked before in IOS12, no longer work. The troubling part is that each mobile device has a separate variable time where it will work. Same application but if a setTimeout, of a variable time from 500ms to 4 seconds depending on the device, is used before the first call it does work. This suggests that the stack is smaller on startup for some reason and then becomes available later? Could this be something that is caused by the JITing/tiered JITing of the WebAssembly module when loading? The stack that is available is being used by the compilation of the WebAssembly module and then freed back up again? Is this controllable somehow? Maybe some type of signal that says the WebAssembly module has finished doing what it is doing on mobile devices and can be called into now. Other than onRuntimeIntialized of course as we are using that to start up the app now. Which stack is actually exceeded to be exact. Is it the JavaScript stack depth or the WebAssembly stack depth? We get the same information for the different stack sizes reported no matter what browser we use. For example: WASM: TOTALMEMORY: 33554432 TOTALSTACK: 5242880 STACKMAX: 726800 STACKTOP: 5969680 STACKBASE: 5969680 The above line is exactly the same across all browsers including safari desktop yet we are running into some limit on mobile safari but not sure exactly where. Any information on the above would be greatly appreciated. Thanks in advance. Just to clarify: The maximum stack depth for WASM in Safari appears to change depending on elapsed time, which would seem to be caused by the tiered JIT. It makes sense that JIT behavior would have changed between 12 and 13, especially because I've seen posts on the WebKit blog to suggest that changes have occurred. The amount of time we need to wait seems to vary across devices (likely based on specs?) which adds weight to our theory that this is based on tiered jit. From examining some of the details of the first tier (BBQ - build bytecode quickly? I can't remember the name) it seems like it could end up generating more stack bloat for complex functions than the final tier does. We have some relatively complex functions (an interpreter, etc) that should normally be very light but are exceeding stack in Safari. Basic tests with simple modules show that we can recurse quite far so it doesn't seem like we're hitting any trivial stack limits here - WASM can in fact recurse much further in Safari than it can in Chrome or Firefox - but instead the code generation seems to be really bad for some corner case we're hitting here. Is there a way to manually reproduce the behavior of the first tier jit without the later tiers? I saw a mix of compile flags when digging through the WebKit source but it's very hard to understand all these systems, there aren't particularly detailed comments and I couldn't find any documentation of what flags iOS or OS X safari build JSC with. (In reply to Saam Barati from comment #3) > All browsers have stack depth limits. Is your bug report just that our limit > has become smaller? Yeah Saam that's what I meant. Basically it seems that the stack limit was reduced in the newer version or there was some other logic change that affected the stack limit as is being discussed in comments I measured the call stack depth using the following simple program: ///////// void recurseHere(int cnt) { if (cnt % 100 == 0) printf("recurseHere: %d\n", cnt); recurseHere(++cnt); } int main() { recurseHere(0); return 0; } //////// I also measured the max stack size via a different simple program. Below are the results. Max Call Stack Depth | Max Stack Size | OS | Browser | Device 11,000+ | 5242848 |iOS 12.3.1 | Safari | iPad Pro 10.5” 900+ | 5242848 |iOS 13.2 | Safari | iPad Pro 12.9” 900+ | 5242848 |iOS 13.2 | Chrome | iPad Pro 12.9” 17,800+ | 5242848 |macOS 10.15.1| Chrome | MacBook 16,700+ | 5242848 |macOS 10.15.1| Safari | MacBook iOS 13 has an order of magnitude less 'Max Call Stack Depth' from iOS 12, even though the 'Max Stack Size' is the same. I tried throwing in different Sleep() calls to see if it effected it as other comments suggested but didn't see any difference. The massive drop in iOS 13 explains a lot - I wasn't able to test that and don't have an iOS 12 device to compare. We were observing regressions in desktop Safari as well, which I couldn't reproduce. The Sleep solution is only going to work for very very large modules (ours are in the dozens of MB) and the fact that the required delay changes between devices suggests that it's based on compile speeds or something. Interesting to see a stack depth limit of 15k for you. I approached around 80k in Safari on Catalina w/a recursive function that accepts 4 int32 arguments. Chrome and Firefox don't go as far, and their limits are dramatically different between Catalina and Windows 10. I suspect there's variability here based on other factors. Being able to dump the generated JITcode for our functions would help us identify what's going on, but I haven't been able to find a way to do this other than doing some really complex trickery in a debugger - only possible on PC, not iOS - or compiling JSC from source, which would require us to know the exact build configuration Safari uses. The differences in stack depth could possibly be because of differences in compilation options. I'm using all defaults for my wasm test apps `emcc hello_world.cpp -o hello.html`. Maybe compiling with more optimizations increases it. As a final test I updated the one iPad to iOS 13.2 from 12.3.1 and confirmed the Max Call Stack depth dropped to 900+ from 11,000+. Here's an updated table: Max Call Stack Depth | Max Stack Size | OS | Browser | Device 11,000+ | 5242848 |iOS 12.3.1 | Safari | iPad Pro 10.5” **900+ | 5242848 |iOS 13.2 | Safari | iPad Pro 10.5” 900+ | 5242848 |iOS 13.2 | Safari | iPad Pro 12.9” 900+ | 5242848 |iOS 13.2 | Chrome | iPad Pro 12.9” 17,800+ | 5242848 |macOS 10.15.1| Chrome | MacBook 16,700+ | 5242848 |macOS 10.15.1| Safari | MacBook I think this is fixed one year ago. |