WebAssembly: restore cached stack limit after out-call
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: http://svn.webkit.org/repository/webkit/trunk@225411 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/wasm/WasmInstance.h b/Source/JavaScriptCore/wasm/WasmInstance.h
index 24a7a24..2c08000 100644
--- a/Source/JavaScriptCore/wasm/WasmInstance.h
+++ b/Source/JavaScriptCore/wasm/WasmInstance.h
@@ -44,7 +44,7 @@
 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 @@
     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 @@
     }
 
 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 @@
     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 };