WebAssembly: perform stack checks
https://bugs.webkit.org/show_bug.cgi?id=165546
<rdar://problem/29760307>

Reviewed by Filip Pizlo.

JSTests:

* wasm.yaml:
* wasm/function-tests/factorial.js:
* wasm/function-tests/float-sub.js:
* wasm/function-tests/stack-overflow.js: Added.
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.assertOverflows):
(assertOverflows.makeInstance):
(assertOverflows.makeInstance2):
(assertOverflows.assertThrows):
(assertOverflows):
(assertThrows.test.makeSignature):
(assertThrows.test.makeInstance):
(assertThrows.test):
(assertThrows):

Source/JavaScriptCore:

This patch adds stack checks to wasm. It implements it by storing the stack
bounds on the Context.
        
Stack checking works as normal, except we do a small optimization for terminal
nodes in the call tree (nodes that don't make any calls). These nodes will
only do a stack check if their frame size is beyond 1024 bytes. Otherwise,
it's assumed the parent that called them did their stack check for them.
This is because all things that make calls make sure to do an extra 1024
bytes whenever doing a stack check.
        
We also take into account stack size for potential JS calls when doing
stack checks since our JS stubs don't do this on their own. Each frame
will ensure it does a stack check large enough for any potential JS call
stubs it'll execute.
        
Surprisingly, this patch is neutral on WasmBench and TitzerBench.

* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LowLevelInterpreter.asm:
* runtime/Error.cpp:
(JSC::createRangeError):
(JSC::addErrorInfoAndGetBytecodeOffset):
I fixed a bug here where we assumed that the first frame that has line
and column info would be in our stack trace. This is not correct
since we limit our stack trace size. If everything in our limited
size stack trace is Wasm, then we won't have any frames with line
and column info.
* runtime/Error.h:
* runtime/ExceptionHelpers.cpp:
(JSC::createStackOverflowError):
* runtime/ExceptionHelpers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::webAssemblyToJSCalleeStructure):
* runtime/JSType.h:
* runtime/Options.h: I've added a new option that controls
whether or not we use fast TLS for the wasm context.
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
* wasm/WasmBinding.cpp:
(JSC::Wasm::wasmToWasm):
* wasm/WasmContext.cpp:
(JSC::Wasm::loadContext):
(JSC::Wasm::storeContext):
* wasm/WasmContext.h:
(JSC::Wasm::useFastTLSForContext):
* wasm/WasmExceptionType.h:
* wasm/WasmMemoryInformation.h:
(JSC::Wasm::PinnedRegisterInfo::toSave):
* wasm/WasmThunks.cpp:
(JSC::Wasm::throwExceptionFromWasmThunkGenerator):
(JSC::Wasm::throwStackOverflowFromWasmThunkGenerator):
(JSC::Wasm::Thunks::stub):
* wasm/WasmThunks.h:
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::offsetOfCachedStackLimit):
(JSC::JSWebAssemblyInstance::cachedStackLimit):
(JSC::JSWebAssemblyInstance::setCachedStackLimit):
* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::finishCreation):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):
* wasm/js/WebAssemblyToJSCallee.cpp: Make this a descendent of object.
This is needed for correctness because we may call into JS,
and then the first JS frame could stack overflow. When it stack
overflows, it rolls back one frame to the wasm->js call stub with
the wasm->js callee. It gets the lexical global object from this
frame, meaning it gets the global object from the callee. Therefore,
we must make it an object since all objects have global objects.
(JSC::WebAssemblyToJSCallee::create):
* wasm/js/WebAssemblyToJSCallee.h:

Tools:

Add some new testing modes for using and not using fast TLS wasm contexts.

* Scripts/run-jsc-stress-tests:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@217060 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/wasm/WasmBinding.cpp b/Source/JavaScriptCore/wasm/WasmBinding.cpp
index 1c73cf6..42a7849 100644
--- a/Source/JavaScriptCore/wasm/WasmBinding.cpp
+++ b/Source/JavaScriptCore/wasm/WasmBinding.cpp
@@ -36,6 +36,7 @@
 #include "LinkBuffer.h"
 #include "NativeErrorConstructor.h"
 #include "WasmCallingConvention.h"
+#include "WasmContext.h"
 #include "WasmExceptionType.h"
 
 namespace JSC { namespace Wasm {
@@ -608,23 +609,29 @@
     JIT jit;
 
     GPRReg scratch = GPRInfo::nonPreservedNonArgumentGPR;
-
-    // B3's call codegen ensures that the JSCell is a WebAssemblyFunction.
-    materializeImportJSCell(jit, importIndex, scratch);
-
-    // Get the callee's WebAssembly.Instance and set it as WasmContext. The caller will take care of restoring its own Instance.
     GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
     ASSERT(baseMemory != scratch);
-    jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*.
-    jit.storeWasmContext(baseMemory);
-
-    // FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952
-    // Set up the callee's baseMemory register as well as the memory size registers.
-    jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
     const auto& sizeRegs = pinnedRegs.sizeRegisters;
     ASSERT(sizeRegs.size() >= 1);
     ASSERT(sizeRegs[0].sizeRegister != baseMemory);
     ASSERT(sizeRegs[0].sizeRegister != scratch);
+    GPRReg sizeRegAsScratch = sizeRegs[0].sizeRegister;
+
+    static_assert(std::is_same<Context, JSWebAssemblyInstance>::value, "This is assumed in the code below.");
+    // B3's call codegen ensures that the JSCell is a WebAssemblyFunction.
+    jit.loadWasmContext(sizeRegAsScratch); // Old Instance*
+    jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), scratch);
+
+    // Get the callee's WebAssembly.Instance and set it as WasmContext. The caller will take care of restoring its own Instance.
+    jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*.
+    jit.storeWasmContext(baseMemory);
+
+    jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfCachedStackLimit()), sizeRegAsScratch);
+    jit.storePtr(sizeRegAsScratch, JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfCachedStackLimit()));
+
+    // FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952
+    // Set up the callee's baseMemory register as well as the memory size registers.
+    jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
     ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
     jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size.
     jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*.