WebAssembly: restore cached stack limit after out-call
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Dec 2017 21:58:36 +0000 (21:58 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Dec 2017 21:58:36 +0000 (21:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179106
<rdar://problem/35337525>

Reviewed by Saam Barati.

JSTests:

* wasm/function-tests/double-instance.js: Added.
(const.imp.boom):
(const.imp.get callAnother):

Source/JavaScriptCore:

We cache the stack limit on the Instance so that we can do fast
stack checks where required. In regular usage the stack limit
never changes because we always run on the same thread, but in
rare cases an API user can totally migrate which thread (and
therefore stack) is used for execution between WebAssembly
traces. For that reason we set the cached stack limit to
UINTPTR_MAX on the outgoing Instance when transitioning back into
a different Instance. We usually restore the cached stack limit in
Context::store, but this wasn't called on all code paths. We had a
bug where an Instance calling into itself indirectly would
therefore fail to restore its cached stack limit properly.

This patch therefore restores the cached stack limit after direct
calls which could be to imports (both wasm->wasm and
wasm->embedder). We have to do all of them because we have no way
of knowing what imports will do (they're known at instantiation
time, not compilation time, and different instances can have
different imports). To make this efficient we also add a pointer
to the canonical location of the stack limit (i.e. the extra
indirection we're trying to save by caching the stack limit on the
Instance in the first place). This is potentially a small perf hit
on imported direct calls.

It's hard to say what the performance cost will be because we
haven't seen much code in the wild which does this. We're adding
two dependent loads and a store of the loaded value, which is
unlikely to get used soon after. It's more code, but on an
out-of-order processor it doesn't contribute to the critical path.

* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::restoreWebAssemblyGlobalState):
(JSC::Wasm::B3IRGenerator::addGrowMemory):
(JSC::Wasm::B3IRGenerator::addCall):
(JSC::Wasm::B3IRGenerator::addCallIndirect):
* wasm/WasmInstance.cpp:
(JSC::Wasm::Instance::Instance):
(JSC::Wasm::Instance::create):
* wasm/WasmInstance.h:
(JSC::Wasm::Instance::offsetOfPointerToActualStackLimit):
(JSC::Wasm::Instance::cachedStackLimit const):
(JSC::Wasm::Instance::setCachedStackLimit):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::create):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@225411 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/wasm/function-tests/double-instance.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmInstance.cpp
Source/JavaScriptCore/wasm/WasmInstance.h
Source/JavaScriptCore/wasm/WasmThunks.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp

index 7f6b0f8..eec284d 100644 (file)
@@ -1,3 +1,15 @@
+2017-12-01  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly: restore cached stack limit after out-call
+        https://bugs.webkit.org/show_bug.cgi?id=179106
+        <rdar://problem/35337525>
+
+        Reviewed by Saam Barati.
+
+        * wasm/function-tests/double-instance.js: Added.
+        (const.imp.boom):
+        (const.imp.get callAnother):
+
 2017-11-30  JF Bastien  <jfbastien@apple.com>
 
         WebAssembly: improve stack trace
diff --git a/JSTests/wasm/function-tests/double-instance.js b/JSTests/wasm/function-tests/double-instance.js
new file mode 100644 (file)
index 0000000..0c4eadd
--- /dev/null
@@ -0,0 +1,64 @@
+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+// The call sequence is as follows:
+//
+// js -js2wasm-> i1.callAnother()
+//               -wasm2wasm-> i0.callAnother()
+//                            -wasm2js-> i1.boom()
+//                                       -calldirect-> i1.doStackCheck()
+//                                                     -calldirect-> dummy()
+//               -calldirect-> i1.doStackCheck()
+//                             -calldirect-> dummy()
+//
+// We therefore have i1 indirectly calling into itself, through another
+// instance. When returning its cached stack limit should still be valid, but
+// our implementation used to set it to UINTPTR_MAX causing an erroneous stack
+// check failure at the second doStackCheck() call.
+
+const builder = new Builder()
+    .Type()
+    .End()
+    .Import()
+        .Function("imp", "boom", {params:["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret:"i32"})
+        .Function("imp", "callAnother", {params:["i32"], ret:"i32"})
+    .End()
+    .Function().End()
+    .Export()
+        .Function("boom")
+        .Function("callAnother")
+    .End()
+    .Code()
+        .Function("boom", {params:["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret:"i32"})
+            /* call doStackCheck */.GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).Call(4)
+            .Return()
+        .End()
+        .Function("callAnother", {params:["i32"], ret:"i32"})
+            /* call imp:callAnother */.GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).Call(1)
+            /* call doStackCheck */.GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).Call(4)
+            .Return()
+        .End()
+        .Function("doStackCheck", {params:["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret:"i32"})
+            /* call dummy */.GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).GetLocal(0).Call(5)
+            .Return()
+        .End()
+        .Function("dummy", {params:["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32"], ret:"i32"})
+            .GetLocal(0)
+            .Return()
+        .End()
+    .End();
+
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+
+let i1;
+
+const imp = {
+    boom: () => { throw new Error(`This boom should not get called!`); },
+    callAnother: () => { i1.exports["boom"](0xdeadbeef); },
+}
+
+const i0 = new WebAssembly.Instance(module, { imp });
+i1 = new WebAssembly.Instance(module, { imp: i0.exports });
+
+i1.exports["callAnother"](0xc0defefe);
index 49c95ce..d7c3342 100644 (file)
@@ -1,3 +1,57 @@
+2017-12-01  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly: restore cached stack limit after out-call
+        https://bugs.webkit.org/show_bug.cgi?id=179106
+        <rdar://problem/35337525>
+
+        Reviewed by Saam Barati.
+
+        We cache the stack limit on the Instance so that we can do fast
+        stack checks where required. In regular usage the stack limit
+        never changes because we always run on the same thread, but in
+        rare cases an API user can totally migrate which thread (and
+        therefore stack) is used for execution between WebAssembly
+        traces. For that reason we set the cached stack limit to
+        UINTPTR_MAX on the outgoing Instance when transitioning back into
+        a different Instance. We usually restore the cached stack limit in
+        Context::store, but this wasn't called on all code paths. We had a
+        bug where an Instance calling into itself indirectly would
+        therefore fail to restore its cached stack limit properly.
+
+        This patch therefore restores the cached stack limit after direct
+        calls which could be to imports (both wasm->wasm and
+        wasm->embedder). We have to do all of them because we have no way
+        of knowing what imports will do (they're known at instantiation
+        time, not compilation time, and different instances can have
+        different imports). To make this efficient we also add a pointer
+        to the canonical location of the stack limit (i.e. the extra
+        indirection we're trying to save by caching the stack limit on the
+        Instance in the first place). This is potentially a small perf hit
+        on imported direct calls.
+
+        It's hard to say what the performance cost will be because we
+        haven't seen much code in the wild which does this. We're adding
+        two dependent loads and a store of the loaded value, which is
+        unlikely to get used soon after. It's more code, but on an
+        out-of-order processor it doesn't contribute to the critical path.
+
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::restoreWebAssemblyGlobalState):
+        (JSC::Wasm::B3IRGenerator::addGrowMemory):
+        (JSC::Wasm::B3IRGenerator::addCall):
+        (JSC::Wasm::B3IRGenerator::addCallIndirect):
+        * wasm/WasmInstance.cpp:
+        (JSC::Wasm::Instance::Instance):
+        (JSC::Wasm::Instance::create):
+        * wasm/WasmInstance.h:
+        (JSC::Wasm::Instance::offsetOfPointerToActualStackLimit):
+        (JSC::Wasm::Instance::cachedStackLimit const):
+        (JSC::Wasm::Instance::setCachedStackLimit):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::create):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction):
+
 2017-11-30  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Use JSFixedArray for op_new_array_buffer
index c00eeca..a2b813f 100644 (file)
@@ -246,7 +246,8 @@ private:
     int32_t WARN_UNUSED_RETURN fixupPointerPlusOffset(ExpressionType&, uint32_t);
 
     void restoreWasmContextInstance(Procedure&, BasicBlock*, Value*);
-    void restoreWebAssemblyGlobalState(const MemoryInformation&, Value* instance, Procedure&, BasicBlock*);
+    enum class RestoreCachedStackLimit { No, Yes };
+    void restoreWebAssemblyGlobalState(RestoreCachedStackLimit, const MemoryInformation&, Value* instance, Procedure&, BasicBlock*);
 
     Origin origin();
 
@@ -450,10 +451,17 @@ B3IRGenerator::B3IRGenerator(const ModuleInformation& info, Procedure& procedure
     emitTierUpCheck(TierUpCount::functionEntryDecrement(), Origin());
 }
 
-void B3IRGenerator::restoreWebAssemblyGlobalState(const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)
+void B3IRGenerator::restoreWebAssemblyGlobalState(RestoreCachedStackLimit restoreCachedStackLimit, const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)
 {
     restoreWasmContextInstance(proc, block, instance);
 
+    if (restoreCachedStackLimit == RestoreCachedStackLimit::Yes) {
+        // The Instance caches the stack limit, but also knows where its canonical location is.
+        Value* pointerToActualStackLimit = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfPointerToActualStackLimit()));
+        Value* actualStackLimit = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), pointerToActualStackLimit);
+        m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin(), actualStackLimit, instanceValue(), safeCast<int32_t>(Instance::offsetOfCachedStackLimit()));
+    }
+
     if (!!memory) {
         const PinnedRegisterInfo* pinnedRegs = &PinnedRegisterInfo::get();
         RegisterSet clobbers;
@@ -583,7 +591,7 @@ auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result)
         m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), bitwise_cast<void*>(growMemory)),
         m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, origin()), instanceValue(), delta);
 
-    restoreWebAssemblyGlobalState(m_info.memory, instanceValue(), m_proc, m_currentBlock);
+    restoreWebAssemblyGlobalState(RestoreCachedStackLimit::No, m_info.memory, instanceValue(), m_proc, m_currentBlock);
 
     return { };
 }
@@ -1149,7 +1157,7 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature,
         }
 
         // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
-        restoreWebAssemblyGlobalState(m_info.memory, instanceValue(), m_proc, continuation);
+        restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, instanceValue(), m_proc, continuation);
     } else {
         result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, origin(), args, toB3Type(returnType),
             [=] (PatchpointValue* patchpoint) {
@@ -1308,7 +1316,7 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio
         });
 
     // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
-    restoreWebAssemblyGlobalState(m_info.memory, instanceValue(), m_proc, m_currentBlock);
+    restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, instanceValue(), m_proc, m_currentBlock);
 
     return { };
 }
index 1664d62..dcbaa35 100644 (file)
@@ -41,11 +41,12 @@ size_t globalMemoryByteSize(Module& module)
 }
 }
 
-Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer, StoreTopCallFrameCallback&& storeTopCallFrame)
+Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame)
     : m_context(context)
     , m_module(WTFMove(module))
     , m_globals(MallocPtr<uint64_t>::malloc(globalMemoryByteSize(m_module.get())))
-    , m_topEntryFramePointer(topEntryFramePointer)
+    , m_pointerToTopEntryFrame(pointerToTopEntryFrame)
+    , m_pointerToActualStackLimit(pointerToActualStackLimit)
     , m_storeTopCallFrame(WTFMove(storeTopCallFrame))
     , m_numImportFunctions(m_module->moduleInformation().importFunctionCount())
 {
@@ -53,9 +54,9 @@ Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** topEntry
         new (importFunctionInfo(i)) ImportFunctionInfo();
 }
 
-Ref<Instance> Instance::create(Context* context, Ref<Module>&& module, EntryFrame** topEntryFramePointer, StoreTopCallFrameCallback&& storeTopCallFrame)
+Ref<Instance> Instance::create(Context* context, Ref<Module>&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame)
 {
-    return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount()))) Instance(context, WTFMove(module), topEntryFramePointer, WTFMove(storeTopCallFrame)));
+    return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount()))) Instance(context, WTFMove(module), pointerToTopEntryFrame, pointerToActualStackLimit, WTFMove(storeTopCallFrame)));
 }
 
 Instance::~Instance() { }
index 24a7a24..2c08000 100644 (file)
@@ -44,7 +44,7 @@ class Instance : public ThreadSafeRefCounted<Instance> {
 public:
     using StoreTopCallFrameCallback = WTF::Function<void(void*)>;
 
-    static Ref<Instance> create(Context*, Ref<Module>&&, EntryFrame** topEntryFramePointer, StoreTopCallFrameCallback&&);
+    static Ref<Instance> create(Context*, Ref<Module>&&, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&&);
 
     void finalizeCreation(void* owner, Ref<CodeBlock>&& codeBlock)
     {
@@ -78,11 +78,20 @@ public:
     static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Instance, m_memory); }
     static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(Instance, m_globals); }
     static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(Instance, m_table); }
-    static ptrdiff_t offsetOfTopEntryFramePointer() { return OBJECT_OFFSETOF(Instance, m_topEntryFramePointer); }
+    static ptrdiff_t offsetOfPointerToTopEntryFrame() { return OBJECT_OFFSETOF(Instance, m_pointerToTopEntryFrame); }
 
+    static ptrdiff_t offsetOfPointerToActualStackLimit() { return OBJECT_OFFSETOF(Instance, m_pointerToActualStackLimit); }
     static ptrdiff_t offsetOfCachedStackLimit() { return OBJECT_OFFSETOF(Instance, m_cachedStackLimit); }
-    void* cachedStackLimit() const { return m_cachedStackLimit; }
-    void setCachedStackLimit(void* limit) { m_cachedStackLimit = limit; }
+    void* cachedStackLimit() const
+    {
+        ASSERT(*m_pointerToActualStackLimit == m_cachedStackLimit);
+        return m_cachedStackLimit;
+    }
+    void setCachedStackLimit(void* limit)
+    {
+        ASSERT(*m_pointerToActualStackLimit == limit || bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()) == limit);
+        m_cachedStackLimit = limit;
+    }
 
     // Tail accessors.
     static size_t offsetOfTail() { return WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(Instance)); }
@@ -111,7 +120,7 @@ public:
     }
 
 private:
-    Instance(Context*, Ref<Module>&&, EntryFrame**, StoreTopCallFrameCallback&&);
+    Instance(Context*, Ref<Module>&&, EntryFrame**, void**, StoreTopCallFrameCallback&&);
     
     static size_t allocationSize(Checked<size_t> numImportFunctions)
     {
@@ -125,7 +134,8 @@ private:
     RefPtr<Memory> m_memory;
     RefPtr<Table> m_table;
     MallocPtr<uint64_t> m_globals;
-    EntryFrame** m_topEntryFramePointer { nullptr };
+    EntryFrame** m_pointerToTopEntryFrame { nullptr };
+    void** m_pointerToActualStackLimit { nullptr };
     void* m_cachedStackLimit { bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()) };
     StoreTopCallFrameCallback m_storeTopCallFrame;
     unsigned m_numImportFunctions { 0 };
index eba6f54..3d143da 100644 (file)
@@ -47,7 +47,7 @@ MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&
     // The thing that jumps here must move ExceptionType into the argumentGPR1 before jumping here.
     // We're allowed to use temp registers here. We are not allowed to use callee saves.
     jit.loadWasmContextInstance(GPRInfo::argumentGPR2);
-    jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, Instance::offsetOfTopEntryFramePointer()), GPRInfo::argumentGPR0);
+    jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, Instance::offsetOfPointerToTopEntryFrame()), GPRInfo::argumentGPR0);
     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0), GPRInfo::argumentGPR0);
     jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(GPRInfo::argumentGPR0);
     jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
index bc6ff04..5c916dd 100644 (file)
@@ -170,7 +170,7 @@ JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, ExecState* exec, JS
 
     // FIXME: These objects could be pretty big we should try to throw OOM here.
     auto* jsInstance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap)) JSWebAssemblyInstance(vm, instanceStructure, 
-        Wasm::Instance::create(&vm.wasmContext, WTFMove(module), &vm.topEntryFrame, WTFMove(storeTopCallFrame)));
+        Wasm::Instance::create(&vm.wasmContext, WTFMove(module), &vm.topEntryFrame, vm.addressOfSoftStackLimit(), WTFMove(storeTopCallFrame)));
     jsInstance->finishCreation(vm, jsModule, moduleNamespace);
     RETURN_IF_EXCEPTION(throwScope, nullptr);
 
index a7ea624..13cfc1c 100644 (file)
@@ -150,7 +150,9 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
         // 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.
+        // fire. The stack limit never changes while executing except when
+        // WebAssembly is used through the JSC API: API users can ask the code
+        // to migrate threads.
         wasmInstance->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
     }
     vm.wasmContext.store(prevWasmInstance, vm.softStackLimit());