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/ChangeLog b/Source/JavaScriptCore/ChangeLog
index d8a3ff8..8704a05 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,89 @@
+2017-05-18  Saam Barati  <sbarati@apple.com>
+
+        WebAssembly: perform stack checks
+        https://bugs.webkit.org/show_bug.cgi?id=165546
+        <rdar://problem/29760307>
+
+        Reviewed by Filip Pizlo.
+
+        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:
+
 2017-05-18  Keith Miller  <keith_miller@apple.com>
 
         WebAssembly API: test with neutered inputs
diff --git a/Source/JavaScriptCore/llint/LLIntData.cpp b/Source/JavaScriptCore/llint/LLIntData.cpp
index 0aff108..8b615a1 100644
--- a/Source/JavaScriptCore/llint/LLIntData.cpp
+++ b/Source/JavaScriptCore/llint/LLIntData.cpp
@@ -156,21 +156,21 @@
     
     STATIC_ASSERT(StringType == 6);
     STATIC_ASSERT(SymbolType == 7);
-    STATIC_ASSERT(ObjectType == 24);
-    STATIC_ASSERT(FinalObjectType == 25);
-    STATIC_ASSERT(JSFunctionType == 27);
-    STATIC_ASSERT(ArrayType == 35);
-    STATIC_ASSERT(DerivedArrayType == 36);
-    STATIC_ASSERT(ProxyObjectType == 54);
-    STATIC_ASSERT(Int8ArrayType == 37);
-    STATIC_ASSERT(Int16ArrayType == 38);
-    STATIC_ASSERT(Int32ArrayType == 39);
-    STATIC_ASSERT(Uint8ArrayType == 40);
-    STATIC_ASSERT(Uint8ClampedArrayType == 41);
-    STATIC_ASSERT(Uint16ArrayType == 42);
-    STATIC_ASSERT(Uint32ArrayType == 43);
-    STATIC_ASSERT(Float32ArrayType == 44);
-    STATIC_ASSERT(Float64ArrayType == 45);
+    STATIC_ASSERT(ObjectType == 23);
+    STATIC_ASSERT(FinalObjectType == 24);
+    STATIC_ASSERT(JSFunctionType == 26);
+    STATIC_ASSERT(ArrayType == 34);
+    STATIC_ASSERT(DerivedArrayType == 35);
+    STATIC_ASSERT(ProxyObjectType == 53);
+    STATIC_ASSERT(Int8ArrayType == 36);
+    STATIC_ASSERT(Int16ArrayType == 37);
+    STATIC_ASSERT(Int32ArrayType == 38);
+    STATIC_ASSERT(Uint8ArrayType == 39);
+    STATIC_ASSERT(Uint8ClampedArrayType == 40);
+    STATIC_ASSERT(Uint16ArrayType == 41);
+    STATIC_ASSERT(Uint32ArrayType == 42);
+    STATIC_ASSERT(Float32ArrayType == 43);
+    STATIC_ASSERT(Float64ArrayType == 44);
     STATIC_ASSERT(MasqueradesAsUndefined == 1);
     STATIC_ASSERT(ImplementsDefaultHasInstance == 2);
     STATIC_ASSERT(FirstConstantRegisterIndex == 0x40000000);
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
index 3da98be..da9c24e 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
@@ -345,24 +345,24 @@
 # Type constants.
 const StringType = 6
 const SymbolType = 7
-const ObjectType = 24
-const FinalObjectType = 25
-const JSFunctionType = 27
-const ArrayType = 35
-const DerivedArrayType = 36
-const ProxyObjectType = 54
+const ObjectType = 23
+const FinalObjectType = 24
+const JSFunctionType = 26
+const ArrayType = 34
+const DerivedArrayType = 35
+const ProxyObjectType = 53
 
 # The typed array types need to be numbered in a particular order because of the manually written
 # switch statement in get_by_val and put_by_val.
-const Int8ArrayType = 37
-const Int16ArrayType = 38
-const Int32ArrayType = 39
-const Uint8ArrayType = 40
-const Uint8ClampedArrayType = 41
-const Uint16ArrayType = 42
-const Uint32ArrayType = 43
-const Float32ArrayType = 44
-const Float64ArrayType = 45
+const Int8ArrayType = 36
+const Int16ArrayType = 37
+const Int32ArrayType = 38
+const Uint8ArrayType = 39
+const Uint8ClampedArrayType = 40
+const Uint16ArrayType = 41
+const Uint32ArrayType = 42
+const Float32ArrayType = 43
+const Float64ArrayType = 44
 
 const FirstArrayType = Int8ArrayType
 const LastArrayType = Float64ArrayType
diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp
index 33388c8..9094d52 100644
--- a/Source/JavaScriptCore/runtime/Error.cpp
+++ b/Source/JavaScriptCore/runtime/Error.cpp
@@ -60,8 +60,13 @@
 
 JSObject* createRangeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender)
 {
-    ASSERT(!message.isEmpty());
     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    return createRangeError(exec, globalObject, message, appender);
+}
+
+JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender)
+{
+    ASSERT(!message.isEmpty());
     return ErrorInstance::create(exec, globalObject->vm(), globalObject->rangeErrorConstructor()->errorStructure(), message, appender, TypeNothing, true);
 }
 
@@ -182,7 +187,7 @@
             callFrame = functor.foundCallFrame();
             unsigned stackIndex = functor.index();
             *bytecodeOffset = 0;
-            if (stackTrace.at(stackIndex).hasBytecodeOffset())
+            if (stackIndex < stackTrace.size() && stackTrace.at(stackIndex).hasBytecodeOffset())
                 *bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset();
         }
         
@@ -268,6 +273,11 @@
     return createRangeError(exec, message, nullptr);
 }
 
+JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message)
+{
+    return createRangeError(exec, globalObject, message, nullptr);
+}
+
 JSObject* createReferenceError(ExecState* exec, const String& message)
 {
     return createReferenceError(exec, message, nullptr);
diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h
index aae660c..330186f 100644
--- a/Source/JavaScriptCore/runtime/Error.h
+++ b/Source/JavaScriptCore/runtime/Error.h
@@ -52,6 +52,7 @@
 JSObject* createError(ExecState*, const String&, ErrorInstance::SourceAppender);
 JSObject* createEvalError(ExecState*, const String&, ErrorInstance::SourceAppender);
 JSObject* createRangeError(ExecState*, const String&, ErrorInstance::SourceAppender);
+JSObject* createRangeError(ExecState*, JSGlobalObject*, const String&, ErrorInstance::SourceAppender);
 JSObject* createReferenceError(ExecState*, const String&, ErrorInstance::SourceAppender);
 JSObject* createSyntaxError(ExecState*, const String&, ErrorInstance::SourceAppender);
 JSObject* createTypeError(ExecState*, const String&, ErrorInstance::SourceAppender, RuntimeType);
@@ -62,6 +63,7 @@
 JS_EXPORT_PRIVATE JSObject* createError(ExecState*, const String&);
 JS_EXPORT_PRIVATE JSObject* createEvalError(ExecState*, const String&);
 JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, const String&);
+JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, JSGlobalObject*, const String&);
 JS_EXPORT_PRIVATE JSObject* createReferenceError(ExecState*, const String&);
 JS_EXPORT_PRIVATE JSObject* createSyntaxError(ExecState*, const String&);
 JS_EXPORT_PRIVATE JSObject* createTypeError(ExecState*);
diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
index b5539df..4b826be 100644
--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
@@ -69,7 +69,12 @@
 
 JSObject* createStackOverflowError(ExecState* exec)
 {
-    return createRangeError(exec, ASCIILiteral("Maximum call stack size exceeded."));
+    return createStackOverflowError(exec, exec->lexicalGlobalObject());
+}
+
+JSObject* createStackOverflowError(ExecState* exec, JSGlobalObject* globalObject)
+{
+    return createRangeError(exec, globalObject, ASCIILiteral("Maximum call stack size exceeded."));
 }
 
 JSObject* createUndefinedVariableError(ExecState* exec, const Identifier& ident)
diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.h b/Source/JavaScriptCore/runtime/ExceptionHelpers.h
index 08e9d18..0549687 100644
--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.h
+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h
@@ -43,6 +43,7 @@
 JS_EXPORT_PRIVATE bool isTerminatedExecutionException(VM&, Exception*);
 JS_EXPORT_PRIVATE JSObject* createError(ExecState*, JSValue, const String&, ErrorInstance::SourceAppender);
 JS_EXPORT_PRIVATE JSObject* createStackOverflowError(ExecState*);
+JSObject* createStackOverflowError(ExecState*, JSGlobalObject*);
 JSObject* createUndefinedVariableError(ExecState*, const Identifier&);
 JSObject* createTDZError(ExecState*);
 JSObject* createNotAnObjectError(ExecState*, JSValue);
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index 13b5c5b..5eeeef6 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -163,6 +163,7 @@
 #include "WeakMapPrototype.h"
 #include "WeakSetConstructor.h"
 #include "WeakSetPrototype.h"
+#include "WebAssemblyToJSCallee.h"
 #include <wtf/RandomNumber.h>
 
 #if ENABLE(INTL)
@@ -887,6 +888,7 @@
         m_webAssemblyModuleRecordStructure.set(vm, this, WebAssemblyModuleRecord::createStructure(vm, this, m_objectPrototype.get()));
         m_webAssemblyFunctionStructure.set(vm, this, WebAssemblyFunction::createStructure(vm, this, m_functionPrototype.get()));
         m_webAssemblyWrapperFunctionStructure.set(vm, this, WebAssemblyWrapperFunction::createStructure(vm, this, m_functionPrototype.get()));
+        m_webAssemblyToJSCalleeStructure.set(vm, this, WebAssemblyToJSCallee::createStructure(vm, this, jsNull()));
         auto* webAssembly = JSWebAssembly::create(vm, this, m_webAssemblyStructure.get());
         putDirectWithoutTransition(vm, Identifier::fromString(exec, "WebAssembly"), webAssembly, DontEnum);
 
@@ -1270,6 +1272,7 @@
     visitor.append(thisObject->m_webAssemblyModuleRecordStructure);
     visitor.append(thisObject->m_webAssemblyFunctionStructure);
     visitor.append(thisObject->m_webAssemblyWrapperFunctionStructure);
+    visitor.append(thisObject->m_webAssemblyToJSCalleeStructure);
     FOR_EACH_WEBASSEMBLY_CONSTRUCTOR_TYPE(VISIT_SIMPLE_TYPE)
 #endif // ENABLE(WEBASSEMBLY)
 
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h
index 305120c..d3935b9 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -351,6 +351,7 @@
     WriteBarrier<Structure> m_webAssemblyModuleRecordStructure;
     WriteBarrier<Structure> m_webAssemblyFunctionStructure;
     WriteBarrier<Structure> m_webAssemblyWrapperFunctionStructure;
+    WriteBarrier<Structure> m_webAssemblyToJSCalleeStructure;
     FOR_EACH_WEBASSEMBLY_CONSTRUCTOR_TYPE(DEFINE_STORAGE_FOR_SIMPLE_TYPE)
 #endif // ENABLE(WEBASSEMBLY)
 
@@ -621,6 +622,7 @@
     Structure* webAssemblyModuleRecordStructure() const { return m_webAssemblyModuleRecordStructure.get(); }
     Structure* webAssemblyFunctionStructure() const { return m_webAssemblyFunctionStructure.get(); }
     Structure* webAssemblyWrapperFunctionStructure() const { return m_webAssemblyWrapperFunctionStructure.get(); }
+    Structure* webAssemblyToJSCalleeStructure() const { return m_webAssemblyToJSCalleeStructure.get(); }
 #endif // ENABLE(WEBASSEMBLY)
 
     JS_EXPORT_PRIVATE void setRemoteDebuggingEnabled(bool);
diff --git a/Source/JavaScriptCore/runtime/JSType.h b/Source/JavaScriptCore/runtime/JSType.h
index ca5aa63..2f77cd0 100644
--- a/Source/JavaScriptCore/runtime/JSType.h
+++ b/Source/JavaScriptCore/runtime/JSType.h
@@ -53,8 +53,6 @@
     JSSourceCodeType,
     JSScriptFetcherType,
 
-    WebAssemblyToJSCalleeType,
-
     // The ObjectType value must come before any JSType that is a subclass of JSObject.
     ObjectType,
     FinalObjectType,
@@ -101,7 +99,9 @@
 
     ClonedArgumentsType,
 
-    LastJSCObjectType = ClonedArgumentsType,
+    WebAssemblyToJSCalleeType,
+
+    LastJSCObjectType = WebAssemblyToJSCalleeType,
     MaxJSType = 0b11111111,
 };
 
diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h
index 5137af8..9c7d053 100644
--- a/Source/JavaScriptCore/runtime/Options.h
+++ b/Source/JavaScriptCore/runtime/Options.h
@@ -455,6 +455,7 @@
     v(bool, crashIfWebAssemblyCantFastMemory, false, Normal, "If true, we will crash if we can't obtain fast memory for wasm.") \
     v(unsigned, webAssemblyFastMemoryPreallocateCount, 0, Normal, "WebAssembly fast memories can be pre-allocated at program startup and remain cached to avoid fragmentation leading to bounds-checked memory. This number is an upper bound on initial allocation as well as total count of fast memories. Zero means no pre-allocation, no caching, and no limit to the number of runtime allocations.") \
     v(bool, useWebAssemblyFastTLS, true, Normal, "If true, we will try to use fast thread-local storage if available on the current platform.") \
+    v(bool, useFastTLSForWasmContext, true, Normal, "If true (and fast TLS is enabled), we will store context in fast TLS. If false, we will pin it to a register.") \
     v(bool, useCallICsForWebAssemblyToJSCalls, true, Normal, "If true, we will use CallLinkInfo to inline cache Wasm to JS calls.")
 
 
diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp
index b4105f1..eacf522 100644
--- a/Source/JavaScriptCore/runtime/VM.cpp
+++ b/Source/JavaScriptCore/runtime/VM.cpp
@@ -239,7 +239,6 @@
     programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, 0, jsNull()));
     functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, 0, jsNull()));
 #if ENABLE(WEBASSEMBLY)
-    webAssemblyToJSCalleeStructure.set(*this, WebAssemblyToJSCallee::createStructure(*this, 0, jsNull()));
     webAssemblyCodeBlockStructure.set(*this, JSWebAssemblyCodeBlock::createStructure(*this, 0, jsNull()));
 #endif
     moduleProgramExecutableStructure.set(*this, ModuleProgramExecutable::createStructure(*this, 0, jsNull()));
diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h
index 761d7c0..6423188 100644
--- a/Source/JavaScriptCore/runtime/VM.h
+++ b/Source/JavaScriptCore/runtime/VM.h
@@ -321,7 +321,6 @@
     Strong<Structure> programExecutableStructure;
     Strong<Structure> functionExecutableStructure;
 #if ENABLE(WEBASSEMBLY)
-    Strong<Structure> webAssemblyToJSCalleeStructure;
     Strong<Structure> webAssemblyCodeBlockStructure;
 #endif
     Strong<Structure> moduleProgramExecutableStructure;
diff --git a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
index 9f3d8d4..1efc51e 100644
--- a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
+++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
@@ -267,6 +267,8 @@
     GPRReg m_memorySizeGPR { InvalidGPRReg };
     GPRReg m_wasmContextGPR;
     Value* m_instanceValue; // FIXME: make this lazy https://bugs.webkit.org/show_bug.cgi?id=169792
+    bool m_makesCalls { false };
+    uint32_t m_maxNumJSCallArguments { 0 };
 };
 
 // Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3.
@@ -381,9 +383,52 @@
 
     wasmCallingConvention().setupFrameInPrologue(&compilation->wasmCalleeMoveLocation, m_proc, Origin(), m_currentBlock);
 
-    m_currentBlock = emitTierUpCheck(m_currentBlock, TierUpCount::functionEntryDecrement(), Origin());
-
     m_instanceValue = materializeWasmContext(m_currentBlock);
+
+    {
+        B3::Value* framePointer = m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, Origin());
+        B3::PatchpointValue* stackOverflowCheck = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin());
+        stackOverflowCheck->appendSomeRegister(framePointer);
+        stackOverflowCheck->appendSomeRegister(m_instanceValue);
+        stackOverflowCheck->clobber(RegisterSet::macroScratchRegisters());
+        stackOverflowCheck->numGPScratchRegisters = 2;
+        stackOverflowCheck->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+            AllowMacroScratchRegisterUsage allowScratch(jit);
+            GPRReg fp = params[0].gpr();
+            GPRReg context = params[1].gpr();
+            GPRReg scratch1 = params.gpScratch(0);
+            GPRReg scratch2 = params.gpScratch(1);
+
+            const Checked<int32_t> wasmFrameSize = params.proc().frameSize();
+            const unsigned minimumParentCheckSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), 1024);
+            const unsigned extraFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), std::max<uint32_t>(
+                // This allows us to elide stack checks for functions that are terminal nodes in the call
+                // tree, (e.g they don't make any calls) and have a small enough frame size. This works by
+                // having any such terminal node have its parent caller include some extra size in its
+                // own check for it. The goal here is twofold:
+                // 1. Emit less code.
+                // 2. Try to speed things up by skipping stack checks.
+                minimumParentCheckSize,
+                // This allows us to elide stack checks in the Wasm -> JS call IC stub. Since these will
+                // spill all arguments to the stack, we ensure that a stack check here covers the
+                // stack that such a stub would use.
+                (Checked<uint32_t>(m_maxNumJSCallArguments) * sizeof(Register) + jscCallingConvention().headerSizeInBytes()).unsafeGet()
+            ));
+            const int32_t checkSize = m_makesCalls ? (wasmFrameSize + extraFrameSize).unsafeGet() : wasmFrameSize.unsafeGet();
+            // This allows leaf functions to not do stack checks if their frame size is within
+            // certain limits since their caller would have already done the check.
+            if (m_makesCalls || wasmFrameSize >= minimumParentCheckSize) {
+                jit.loadPtr(CCallHelpers::Address(context, Context::offsetOfCachedStackLimit()), scratch2);
+                jit.addPtr(CCallHelpers::TrustedImm32(-checkSize), fp, scratch1);
+                auto overflow = jit.branchPtr(CCallHelpers::Below, scratch1, scratch2);
+                jit.addLinkTask([overflow] (LinkBuffer& linkBuffer) {
+                    linkBuffer.link(overflow, CodeLocationLabel(Thunks::singleton().stub(throwStackOverflowFromWasmThunkGenerator).code()));
+                });
+            }
+        });
+    }
+
+    m_currentBlock = emitTierUpCheck(m_currentBlock, TierUpCount::functionEntryDecrement(), Origin());
 }
 
 void B3IRGenerator::restoreWebAssemblyGlobalState(const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)
@@ -998,10 +1043,14 @@
 {
     ASSERT(signature.argumentCount() == args.size());
 
+    m_makesCalls = true;
+
     Type returnType = signature.returnType();
     Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
 
     if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) {
+        m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
+
         // FIXME imports can be linked here, instead of generating a patchpoint, because all import stubs are generated before B3 compilation starts. https://bugs.webkit.org/show_bug.cgi?id=166462
         Value* functionImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), m_instanceValue, safeCast<int32_t>(JSWebAssemblyInstance::offsetOfImportFunction(functionIndex)));
         Value* jsTypeOfImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin(), functionImport, safeCast<int32_t>(JSCell::typeInfoTypeOffset()));
@@ -1017,7 +1066,9 @@
                 patchpoint->effects.writesPinned = true;
                 patchpoint->effects.readsPinned = true;
                 // We need to clobber all potential pinned registers since we might be leaving the instance.
-                patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
+                // We pessimistically assume we could be calling to something that is bounds checking.
+                // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
+                patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
                 patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
                     AllowMacroScratchRegisterUsage allowScratch(jit);
                     CCallHelpers::Call call = jit.threadSafePatchableNearCall();
@@ -1043,7 +1094,9 @@
                 patchpoint->effects.readsPinned = true;
                 patchpoint->append(jumpDestination, ValueRep::SomeRegister);
                 // We need to clobber all potential pinned registers since we might be leaving the instance.
-                patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
+                // We pessimistically assume we could be calling to something that is bounds checking.
+                // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
+                patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
                 patchpoint->setGenerator([returnType] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
                     AllowMacroScratchRegisterUsage allowScratch(jit);
                     jit.call(params[returnType == Void ? 0 : 1].gpr());
@@ -1088,6 +1141,12 @@
     ExpressionType calleeIndex = args.takeLast();
     ASSERT(signature.argumentCount() == args.size());
 
+    m_makesCalls = true;
+    // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because
+    // WebAssemblyWrapperFunction is like calling into JS, we conservatively assume all call indirects
+    // can be to JS for our stack check calculation.
+    m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
+
     ExpressionType callableFunctionBuffer;
     ExpressionType jsFunctionBuffer;
     ExpressionType callableFunctionBufferSize;
@@ -1168,14 +1227,17 @@
         patchpoint->clobber(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
         patchpoint->clobber(RegisterSet::macroScratchRegisters());
         patchpoint->append(newContext, ValueRep::SomeRegister);
+        patchpoint->append(m_instanceValue, ValueRep::SomeRegister);
         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
             AllowMacroScratchRegisterUsage allowScratch(jit);
             GPRReg newContext = params[0].gpr();
+            GPRReg oldContext = params[1].gpr();
             const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
             const auto& sizeRegs = pinnedRegs.sizeRegisters;
             GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
             ASSERT(newContext != baseMemory);
-
+            jit.loadPtr(CCallHelpers::Address(oldContext, Context::offsetOfCachedStackLimit()), baseMemory);
+            jit.storePtr(baseMemory, CCallHelpers::Address(newContext, Context::offsetOfCachedStackLimit()));
             jit.storeWasmContext(newContext);
             jit.loadPtr(CCallHelpers::Address(newContext, Context::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
             ASSERT(sizeRegs.size() == 1);
@@ -1200,7 +1262,11 @@
             patchpoint->effects.writesPinned = true;
             patchpoint->effects.readsPinned = true;
             // We need to clobber all potential pinned registers since we might be leaving the instance.
-            patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
+            // We pessimistically assume we're always calling something that is bounds checking so
+            // because the wasm->wasm thunk unconditionally overrides the size registers.
+            // FIXME: We should not have to do this, but the wasm->wasm stub assumes it can
+            // use all the pinned registers as scratch: https://bugs.webkit.org/show_bug.cgi?id=172181
+            patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
 
             patchpoint->append(calleeCode, ValueRep::SomeRegister);
             patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
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*.
diff --git a/Source/JavaScriptCore/wasm/WasmContext.cpp b/Source/JavaScriptCore/wasm/WasmContext.cpp
index 998b6aa..fea16de 100644
--- a/Source/JavaScriptCore/wasm/WasmContext.cpp
+++ b/Source/JavaScriptCore/wasm/WasmContext.cpp
@@ -40,7 +40,6 @@
     if (useFastTLSForContext())
         return bitwise_cast<Context*>(_pthread_getspecific_direct(WTF_WASM_CONTEXT_KEY));
 #endif
-    // FIXME: Save this state elsewhere to allow PIC. https://bugs.webkit.org/show_bug.cgi?id=169773
     return vm.wasmContext;
 }
 
@@ -50,8 +49,9 @@
     if (useFastTLSForContext())
         _pthread_setspecific_direct(WTF_WASM_CONTEXT_KEY, bitwise_cast<void*>(context));
 #endif
-    // FIXME: Save this state elsewhere to allow PIC. https://bugs.webkit.org/show_bug.cgi?id=169773
     vm.wasmContext = context;
+    if (context)
+        context->setCachedStackLimit(vm.softStackLimit());
 }
 
 } } // namespace JSC::Wasm
diff --git a/Source/JavaScriptCore/wasm/WasmContext.h b/Source/JavaScriptCore/wasm/WasmContext.h
index 5f0b327..3cf65fb 100644
--- a/Source/JavaScriptCore/wasm/WasmContext.h
+++ b/Source/JavaScriptCore/wasm/WasmContext.h
@@ -51,7 +51,9 @@
 
 inline bool useFastTLSForContext()
 {
-    return useFastTLS();
+    if (useFastTLS())
+        return Options::useFastTLSForWasmContext();
+    return false;
 }
 
 Context* loadContext(VM&);
diff --git a/Source/JavaScriptCore/wasm/WasmExceptionType.h b/Source/JavaScriptCore/wasm/WasmExceptionType.h
index 34bb3d2..28bdf56 100644
--- a/Source/JavaScriptCore/wasm/WasmExceptionType.h
+++ b/Source/JavaScriptCore/wasm/WasmExceptionType.h
@@ -39,7 +39,8 @@
     macro(OutOfBoundsTrunc, "Out of bounds Trunc operation") \
     macro(Unreachable, "Unreachable code should not be executed") \
     macro(DivisionByZero, "Division by zero") \
-    macro(IntegerOverflow, "Integer overflow")
+    macro(IntegerOverflow, "Integer overflow") \
+    macro(StackOverflow, "Stack overflow")
 
 enum class ExceptionType : uint32_t {
 #define MAKE_ENUM(enumName, error) enumName,
diff --git a/Source/JavaScriptCore/wasm/WasmMemoryInformation.h b/Source/JavaScriptCore/wasm/WasmMemoryInformation.h
index eae1037..4237bac 100644
--- a/Source/JavaScriptCore/wasm/WasmMemoryInformation.h
+++ b/Source/JavaScriptCore/wasm/WasmMemoryInformation.h
@@ -48,7 +48,7 @@
     static const PinnedRegisterInfo& get();
     PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&&, GPRReg, GPRReg);
 
-    RegisterSet toSave(MemoryMode mode = MemoryMode::BoundsChecking) const
+    RegisterSet toSave(MemoryMode mode) const
     {
         RegisterSet result;
         result.set(baseMemoryPointer);
diff --git a/Source/JavaScriptCore/wasm/WasmThunks.cpp b/Source/JavaScriptCore/wasm/WasmThunks.cpp
index 2f1cc41..17e8ef4 100644
--- a/Source/JavaScriptCore/wasm/WasmThunks.cpp
+++ b/Source/JavaScriptCore/wasm/WasmThunks.cpp
@@ -40,7 +40,7 @@
 
 namespace JSC { namespace Wasm {
 
-MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator()
+MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&)
 {
     CCallHelpers jit;
 
@@ -62,8 +62,11 @@
             auto throwScope = DECLARE_THROW_SCOPE(*vm);
             JSGlobalObject* globalObject = wasmContext->globalObject();
 
-            JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(
-                exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
+            JSObject* error; 
+            if (type == ExceptionType::StackOverflow)
+                error = createStackOverflowError(exec, globalObject);
+            else
+                error = JSWebAssemblyRuntimeError::create(exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
             throwException(exec, throwScope, error);
         }
 
@@ -86,6 +89,18 @@
     return FINALIZE_CODE(linkBuffer, ("Throw exception from Wasm"));
 }
 
+MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker& locker)
+{
+    CCallHelpers jit;
+
+    jit.move(GPRInfo::callFrameRegister, MacroAssembler::stackPointerRegister);
+    jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(ExceptionType::StackOverflow)), GPRInfo::argumentGPR1);
+    auto jumpToExceptionHandler = jit.jump();
+    LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
+    linkBuffer.link(jumpToExceptionHandler, CodeLocationLabel(Thunks::singleton().stub(locker, throwExceptionFromWasmThunkGenerator).code()));
+    return FINALIZE_CODE(linkBuffer, ("Throw stack overflow from Wasm"));
+}
+
 static Thunks* thunks;
 void Thunks::initialize()
 {
@@ -101,12 +116,22 @@
 MacroAssemblerCodeRef Thunks::stub(ThunkGenerator generator)
 {
     auto locker = holdLock(m_lock);
+    return stub(locker, generator);
+}
 
+MacroAssemblerCodeRef Thunks::stub(const AbstractLocker& locker, ThunkGenerator generator)
+{
     ASSERT(!!generator);
-    auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
-    if (addResult.isNewEntry)
-        addResult.iterator->value = generator();
-    return addResult.iterator->value;
+    {
+        auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
+        if (!addResult.isNewEntry)
+            return addResult.iterator->value;
+    }
+
+    MacroAssemblerCodeRef code = generator(locker);
+    // We specifically don't use the iterator here to allow generator to recursively change m_stubs.
+    m_stubs.set(generator, code);
+    return code;
 }
 
 MacroAssemblerCodeRef Thunks::existingStub(ThunkGenerator generator)
diff --git a/Source/JavaScriptCore/wasm/WasmThunks.h b/Source/JavaScriptCore/wasm/WasmThunks.h
index 65375d6..6bca302 100644
--- a/Source/JavaScriptCore/wasm/WasmThunks.h
+++ b/Source/JavaScriptCore/wasm/WasmThunks.h
@@ -31,9 +31,10 @@
 
 namespace JSC { namespace Wasm {
 
-MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator();
+MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&);
+MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker&);
 
-typedef MacroAssemblerCodeRef (*ThunkGenerator)();
+typedef MacroAssemblerCodeRef (*ThunkGenerator)(const AbstractLocker&);
 
 class Thunks {
 public:
@@ -41,6 +42,7 @@
     static Thunks& singleton();
 
     MacroAssemblerCodeRef stub(ThunkGenerator);
+    MacroAssemblerCodeRef stub(const AbstractLocker&, ThunkGenerator);
     MacroAssemblerCodeRef existingStub(ThunkGenerator);
 
 private:
diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
index f02ac6d..23df6cf 100644
--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
@@ -73,11 +73,15 @@
     static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); }
     static ptrdiff_t offsetOfVM() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_vm); }
     static ptrdiff_t offsetOfCodeBlock() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_codeBlock); }
+    static ptrdiff_t offsetOfCachedStackLimit() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_cachedStackLimit); }
     static size_t offsetOfImportFunctions() { return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance)); }
     static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfImportFunctions() + importFunctionNum * sizeof(sizeof(WriteBarrier<JSCell>)); }
 
     WebAssemblyToJSCallee* webAssemblyToJSCallee() { return m_callee.get(); }
 
+    void* cachedStackLimit() const { return m_cachedStackLimit; }
+    void setCachedStackLimit(void* limit) { m_cachedStackLimit = limit; }
+
 protected:
     JSWebAssemblyInstance(VM&, Structure*, unsigned numImportFunctions);
     void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);
@@ -101,6 +105,7 @@
     WriteBarrier<JSWebAssemblyTable> m_table;
     WriteBarrier<WebAssemblyToJSCallee> m_callee;
     MallocPtr<uint64_t> m_globals;
+    void* m_cachedStackLimit { bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()) };
     unsigned m_numImportFunctions;
 };
 
diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
index 4ac4983..c0726ff 100644
--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
@@ -82,7 +82,7 @@
     }
 
     m_exportSymbolTable.set(vm, this, exportSymbolTable);
-    m_callee.set(vm, this, WebAssemblyToJSCallee::create(vm, vm.webAssemblyToJSCalleeStructure.get(), this));
+    m_callee.set(vm, this, WebAssemblyToJSCallee::create(vm, this));
 }
 
 void JSWebAssemblyModule::destroy(JSCell* cell)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
index e2800bf..8747db3 100644
--- a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
@@ -128,6 +128,15 @@
 
     // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
     Wasm::Context* prevWasmContext = Wasm::loadContext(vm);
+    {
+        // We do the stack check here for the wrapper function because we don't
+        // want to emit a stack check inside every wrapper function.
+        const intptr_t sp = bitwise_cast<intptr_t>(&sp); // A proxy for the current stack pointer.
+        const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
+        const intptr_t stackSpaceUsed = 2 * frameSize; // We're making two calls. One to the wrapper, and one to the actual wasm code.
+        if (UNLIKELY((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit())))
+            return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
+    }
     Wasm::storeContext(vm, wasmContext);
     ASSERT(wasmFunction->instance());
     ASSERT(wasmFunction->instance() == Wasm::loadContext(vm));
@@ -135,7 +144,14 @@
     // We need to make sure this is in a register or on the stack since it's stored in Vector<JSValue>.
     // This probably isn't strictly necessary, since the WebAssemblyFunction* should keep the instance
     // alive. But it's good hygiene.
-    wasmContext->use(); 
+    wasmContext->use();
+    if (prevWasmContext != wasmContext) {
+        // This is just for some extra safety instead of leaving a cached
+        // value in there. If we ever forget to set the value to be a real
+        // bounds, this will force every stack overflow check to immediately
+        // fire.
+        wasmContext->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
+    }
     Wasm::storeContext(vm, prevWasmContext);
     RETURN_IF_EXCEPTION(scope, { });
 
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp
index 4b9c86f..057c579 100644
--- a/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp
+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp
@@ -33,10 +33,11 @@
 
 namespace JSC {
 
-const ClassInfo WebAssemblyToJSCallee::s_info = { "WebAssemblyToJSCallee", nullptr, 0, CREATE_METHOD_TABLE(WebAssemblyToJSCallee) };
+const ClassInfo WebAssemblyToJSCallee::s_info = { "WebAssemblyToJSCallee", &Base::s_info, 0, CREATE_METHOD_TABLE(WebAssemblyToJSCallee) };
 
-WebAssemblyToJSCallee* WebAssemblyToJSCallee::create(VM& vm, Structure* structure, JSWebAssemblyModule* module)
+WebAssemblyToJSCallee* WebAssemblyToJSCallee::create(VM& vm, JSWebAssemblyModule* module)
 {
+    Structure* structure = module->globalObject()->webAssemblyToJSCalleeStructure();
     WebAssemblyToJSCallee* callee = new (NotNull, allocateCell<WebAssemblyToJSCallee>(vm.heap)) WebAssemblyToJSCallee(vm, structure);
     callee->finishCreation(vm, module);
     return callee;
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h
index 19f4795..061d14d 100644
--- a/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h
+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h
@@ -27,18 +27,18 @@
 
 #if ENABLE(WEBASSEMBLY)
 
-#include "JSCell.h"
+#include "JSObject.h"
 
 namespace JSC {
 
 class JSWebAssemblyModule;
 
-class WebAssemblyToJSCallee final : public JSCell {
+class WebAssemblyToJSCallee final : public JSNonFinalObject {
 public:
-    typedef JSCell Base;
-    static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+    using Base = JSNonFinalObject;
+    static const unsigned StructureFlags = Base::StructureFlags;
 
-    static WebAssemblyToJSCallee* create(VM&, Structure*, JSWebAssemblyModule*);
+    static WebAssemblyToJSCallee* create(VM&, JSWebAssemblyModule*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_EXPORT_INFO;