Bug 1389464 - Implement shared memory for WebAssembly. r=luke
authorLars T Hansen <lhansen@mozilla.com>
Fri, 22 Sep 2017 10:49:19 +0200
changeset 393113 5b8da4918cbafd4296c318f54fd6bbd2c9c453c3
parent 393112 d704d1e5b453f463329317dd36a3b3ad5b41ade0
child 393114 565ca96c41490c9367b476d41af9aa6e0b611bd9
push id97587
push userlhansen@mozilla.com
push dateWed, 22 Nov 2017 12:18:33 +0000
treeherdermozilla-inbound@2d6227004721 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1389464
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1389464 - Implement shared memory for WebAssembly. r=luke
js/src/builtin/AtomicsObject.cpp
js/src/js.msg
js/src/shell/js.cpp
js/src/vm/ArrayBufferObject-inl.h
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/MutexIDs.h
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
js/src/vm/StructuredClone.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmInstance.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmJS.h
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmSignalHandlers.cpp
js/src/wasm/WasmTypes.cpp
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -530,22 +530,20 @@ js::atomics_isLockFree(JSContext* cx, un
 //
 // To test this, either run on eg Raspberry Pi Model 1, or invoke the ARM
 // simulator build with ARMHWCAP=vfp set.  Do not set any other flags; other
 // vfp/neon flags force ARMv7 to be set.
 
 int32_t
 js::atomics_add_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformAdd::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformAdd::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformAdd::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -553,22 +551,20 @@ js::atomics_add_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_sub_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformSub::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformSub::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformSub::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -576,22 +572,20 @@ js::atomics_sub_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_and_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformAnd::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformAnd::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformAnd::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -599,22 +593,20 @@ js::atomics_and_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_or_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformOr::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformOr::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformOr::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -622,22 +614,20 @@ js::atomics_or_asm_callout(wasm::Instanc
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_xor_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformXor::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformXor::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformXor::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -645,22 +635,20 @@ js::atomics_xor_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_xchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return ExchangeOrStore<DoExchange>(Scalar::Int8, value, heap, offset);
       case Scalar::Uint8:
         return ExchangeOrStore<DoExchange>(Scalar::Uint8, value, heap, offset);
       case Scalar::Int16:
         return ExchangeOrStore<DoExchange>(Scalar::Int16, value, heap, offset>>1);
       case Scalar::Uint16:
@@ -668,22 +656,20 @@ js::atomics_xchg_asm_callout(wasm::Insta
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_cmpxchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t oldval, int32_t newval)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return CompareExchange(Scalar::Int8, oldval, newval, heap, offset);
       case Scalar::Uint8:
         return CompareExchange(Scalar::Uint8, oldval, newval, heap, offset);
       case Scalar::Int16:
         return CompareExchange(Scalar::Int16, oldval, newval, heap, offset>>1);
       case Scalar::Uint16:
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -356,16 +356,18 @@ MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,       1
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1, JSEXN_WARN,    "Successfully compiled asm.js code ({0})")
 
 // wasm
 MSG_DEF(JSMSG_WASM_COMPILE_ERROR,      1, JSEXN_WASMCOMPILEERROR, "{0}")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_TYPE,    2, JSEXN_WASMLINKERROR, "import object field '{0}' is not a {1}")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG,     2, JSEXN_WASMLINKERROR, "imported function '{0}.{1}' signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE,       1, JSEXN_WASMLINKERROR, "imported {0} with incompatible size")
 MSG_DEF(JSMSG_WASM_BAD_IMP_MAX,        1, JSEXN_WASMLINKERROR, "imported {0} with incompatible maximum size")
+MSG_DEF(JSMSG_WASM_IMP_SHARED_REQD,    0, JSEXN_WASMLINKERROR, "imported unshared memory but shared required")
+MSG_DEF(JSMSG_WASM_IMP_SHARED_BANNED,  0, JSEXN_WASMLINKERROR, "imported shared memory but unshared required")
 MSG_DEF(JSMSG_WASM_BAD_FIT,            2, JSEXN_WASMLINKERROR, "{0} segment does not fit in {1}")
 MSG_DEF(JSMSG_WASM_BAD_I64_LINK,       0, JSEXN_WASMLINKERROR, "cannot pass i64 to or from JS")
 MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL,   0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
 MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG,   0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_WASMRUNTIMEERROR, "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_WASMRUNTIMEERROR, "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_WASMRUNTIMEERROR, "integer divide by zero")
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5593,17 +5593,18 @@ GetSharedArrayBuffer(JSContext* cx, unsi
             if (!buf->addReference()) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
                 return false;
             }
 
             // Shared memory is enabled globally in the shell: there can't be a worker
             // that does not enable it if the main thread has it.
             MOZ_ASSERT(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
-            newObj = SharedArrayBufferObject::New(cx, buf);
+            SharedArrayRawBuffer::Lock l(buf);
+            newObj = SharedArrayBufferObject::New(cx, buf, buf->byteLength(l));
             if (!newObj) {
                 buf->dropReference();
                 return false;
             }
         }
     }
 
     args.rval().setObjectOrNull(newObj);
--- a/js/src/vm/ArrayBufferObject-inl.h
+++ b/js/src/vm/ArrayBufferObject-inl.h
@@ -46,16 +46,24 @@ AnyArrayBufferByteLength(const ArrayBuff
 inline bool
 AnyArrayBufferIsPreparedForAsmJS(const ArrayBufferObjectMaybeShared* buf)
 {
     if (buf->is<ArrayBufferObject>())
         return buf->as<ArrayBufferObject>().isPreparedForAsmJS();
     return buf->as<SharedArrayBufferObject>().isPreparedForAsmJS();
 }
 
+inline bool
+AnyArrayBufferIsWasm(const ArrayBufferObjectMaybeShared* buf)
+{
+    if (buf->is<ArrayBufferObject>())
+        return buf->as<ArrayBufferObject>().isWasm();
+    return buf->as<SharedArrayBufferObject>().isWasm();
+}
+
 inline ArrayBufferObjectMaybeShared&
 AsAnyArrayBuffer(HandleValue val)
 {
     if (val.toObject().is<ArrayBufferObject>())
         return val.toObject().as<ArrayBufferObject>();
     return val.toObject().as<SharedArrayBufferObject>();
 }
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -642,17 +642,17 @@ ArrayBufferObject::changeContents(JSCont
  *  bounds check against base pointers and fold some constant offsets inside
  *  loads. This enables better Bounds Check Elimination.
  *
  */
 
 class js::WasmArrayRawBuffer
 {
     Maybe<uint32_t> maxSize_;
-    size_t mappedSize_;
+    size_t mappedSize_;         // Not including the header page
 
   protected:
     WasmArrayRawBuffer(uint8_t* buffer, uint32_t length, const Maybe<uint32_t>& maxSize,
                        size_t mappedSize)
       : maxSize_(maxSize), mappedSize_(mappedSize)
     {
         MOZ_ASSERT(buffer == dataPointer());
     }
@@ -736,16 +736,18 @@ class js::WasmArrayRawBuffer
         maxSize_ = Some(newMaxSize.value());
     }
 #endif // WASM_HUGE_MEMORY
 };
 
 /* static */ WasmArrayRawBuffer*
 WasmArrayRawBuffer::Allocate(uint32_t numBytes, const Maybe<uint32_t>& maxSize)
 {
+    MOZ_RELEASE_ASSERT(numBytes <= ArrayBufferObject::MaxBufferByteLength);
+
     size_t mappedSize;
 #ifdef WASM_HUGE_MEMORY
     mappedSize = wasm::HugeMappedSize;
 #else
     mappedSize = wasm::ComputeMappedSize(maxSize.valueOr(numBytes));
 #endif
 
     MOZ_RELEASE_ASSERT(mappedSize <= SIZE_MAX - gc::SystemPageSize());
@@ -780,89 +782,112 @@ WasmArrayRawBuffer::Release(void* mem)
 
 WasmArrayRawBuffer*
 ArrayBufferObject::BufferContents::wasmBuffer() const
 {
     MOZ_RELEASE_ASSERT(kind_ == WASM);
     return (WasmArrayRawBuffer*)(data_ - sizeof(WasmArrayRawBuffer));
 }
 
+void
+ArrayBufferObject::initializeRawBuffer(JSContext* cx, WasmArrayRawBuffer* wasmBuf, uint32_t initialSize)
+{
+    auto contents = BufferContents::create<WASM>(wasmBuf->dataPointer());
+    initialize(initialSize, contents, OwnsData);
+    cx->updateMallocCounter(wasmBuf->mappedSize());
+}
+
+template<typename ObjT, typename RawbufT>
+static bool
+CreateBuffer(JSContext* cx, uint32_t initialSize, const Maybe<uint32_t>& maxSize,
+             MutableHandleArrayBufferObjectMaybeShared maybeSharedObject)
+{
+    Rooted<ObjT*> object(cx, ObjT::createEmpty(cx));
+    if (!object)
+        return false;
+
 #define ROUND_UP(v, a) ((v) % (a) == 0 ? (v) : v + a - ((v) % (a)))
 
-/* static */ ArrayBufferObject*
-ArrayBufferObject::createForWasm(JSContext* cx, uint32_t initialSize,
-                                 const Maybe<uint32_t>& maybeMaxSize)
+    RawbufT* buffer = RawbufT::Allocate(initialSize, maxSize);
+    if (!buffer) {
+#ifdef  WASM_HUGE_MEMORY
+        ReportOutOfMemory(cx);
+        return false;
+#else
+        // If we fail, and have a maxSize, try to reserve the biggest chunk in
+        // the range [initialSize, maxSize) using log backoff.
+        if (!maxSize) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+
+        uint32_t cur = maxSize.value() / 2;
+
+        for (; cur > initialSize; cur /= 2) {
+            buffer = RawbufT::Allocate(initialSize, mozilla::Some(ROUND_UP(cur, wasm::PageSize)));
+            if (buffer)
+                break;
+        }
+
+        if (!buffer) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+
+        // Try to grow our chunk as much as possible.
+        for (size_t d = cur / 2; d >= wasm::PageSize; d /= 2)
+            buffer->tryGrowMaxSizeInPlace(ROUND_UP(d, wasm::PageSize));
+#endif
+    }
+
+#undef ROUND_UP
+
+    object->initializeRawBuffer(cx, buffer, initialSize);
+
+    maybeSharedObject.set(object.get());
+    return true;
+}
+
+bool
+js::CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
+                     MutableHandleArrayBufferObjectMaybeShared buffer)
 {
-    MOZ_ASSERT(initialSize % wasm::PageSize == 0);
+    MOZ_ASSERT(memory.initial % wasm::PageSize == 0);
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
-    MOZ_RELEASE_ASSERT((initialSize / wasm::PageSize) <= wasm::MaxMemoryInitialPages);
+    MOZ_RELEASE_ASSERT((memory.initial / wasm::PageSize) <= wasm::MaxMemoryInitialPages);
 
     // Prevent applications specifying a large max (like UINT32_MAX) from
     // unintentially OOMing the browser on 32-bit: they just want "a lot of
     // memory". Maintain the invariant that initialSize <= maxSize.
-    Maybe<uint32_t> maxSize = maybeMaxSize;
-    if (sizeof(void*) == 4 && maybeMaxSize) {
+
+    Maybe<uint32_t> maxSize = memory.maximum;
+    if (sizeof(void*) == 4 && maxSize) {
         static const uint32_t OneGiB = 1 << 30;
-        uint32_t clamp = Max(OneGiB, initialSize);
-        maxSize = Some(Min(clamp, maybeMaxSize.value()));
+        uint32_t clamp = Max(OneGiB, memory.initial);
+        maxSize = Some(Min(clamp, *maxSize));
     }
 
 #ifndef WASM_HUGE_MEMORY
-    if (sizeof(void*) == 8 && maybeMaxSize && maybeMaxSize.value() == UINT32_MAX) {
+    if (sizeof(void*) == 8 && maxSize && maxSize.value() == UINT32_MAX) {
         // On 64-bit platforms that don't define WASM_HUGE_MEMORY
         // clamp maxSize to smaller value that satisfies the 32-bit invariants
         // maxSize + wasm::PageSize < UINT32_MAX and maxSize % wasm::PageSize == 0
         uint32_t clamp = (wasm::MaxMemoryMaximumPages - 2) * wasm::PageSize;
         MOZ_ASSERT(clamp < UINT32_MAX);
-        MOZ_ASSERT(initialSize <= clamp);
+        MOZ_ASSERT(memory.initial <= clamp);
         maxSize = Some(clamp);
     }
 #endif
 
-    RootedArrayBufferObject buffer(cx, ArrayBufferObject::createEmpty(cx));
-    if (!buffer)
-        return nullptr;
-
-    // Try to reserve the maximum requested memory
-    WasmArrayRawBuffer* wasmBuf = WasmArrayRawBuffer::Allocate(initialSize, maxSize);
-    if (!wasmBuf) {
-#ifdef  WASM_HUGE_MEMORY
-        ReportOutOfMemory(cx);
-        return nullptr;
-#else
-        // If we fail, and have a maxSize, try to reserve the biggest chunk in
-        // the range [initialSize, maxSize) using log backoff.
-        if (!maxSize) {
-            ReportOutOfMemory(cx);
-            return nullptr;
-        }
-
-        uint32_t cur = maxSize.value() / 2;
-
-        for (; cur > initialSize; cur /= 2) {
-            wasmBuf = WasmArrayRawBuffer::Allocate(initialSize, Some(ROUND_UP(cur, wasm::PageSize)));
-            if (wasmBuf)
-                break;
-        }
-
-        if (!wasmBuf) {
-            ReportOutOfMemory(cx);
-            return nullptr;
-        }
-
-        // Try to grow our chunk as much as possible.
-        for (size_t d = cur / 2; d >= wasm::PageSize; d /= 2)
-            wasmBuf->tryGrowMaxSizeInPlace(ROUND_UP(d, wasm::PageSize));
-#endif
+    if (memory.shared == wasm::Shareable::True) {
+        return CreateBuffer<SharedArrayBufferObject, SharedArrayRawBuffer>(cx, memory.initial,
+                                                                           maxSize, buffer);
     }
-
-    auto contents = BufferContents::create<WASM>(wasmBuf->dataPointer());
-    buffer->initialize(initialSize, contents, OwnsData);
-    cx->updateMallocCounter(wasmBuf->mappedSize());
-    return buffer;
+    return CreateBuffer<ArrayBufferObject, WasmArrayRawBuffer>(cx, memory.initial, maxSize,
+                                                               buffer);
 }
 
 // Note this function can return false with or without an exception pending. The
 // asm.js caller checks cx->isExceptionPending before propagating failure.
 // Returning false without throwing means that asm.js linking will fail which
 // will recompile as non-asm.js.
 /* static */ bool
 ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool needGuard)
@@ -998,56 +1023,48 @@ ArrayBufferObject::wasmMappedSize() cons
     return byteLength();
 }
 
 size_t
 js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf)
 {
     if (buf->is<ArrayBufferObject>())
         return buf->as<ArrayBufferObject>().wasmMappedSize();
-#ifdef WASM_HUGE_MEMORY
-    return wasm::HugeMappedSize;
-#else
-    return buf->as<SharedArrayBufferObject>().byteLength();
-#endif
+    return buf->as<SharedArrayBufferObject>().wasmMappedSize();
 }
 
 Maybe<uint32_t>
 ArrayBufferObject::wasmMaxSize() const
 {
     if (isWasm())
         return contents().wasmBuffer()->maxSize();
     else
         return Some<uint32_t>(byteLength());
 }
 
 Maybe<uint32_t>
 js::WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf)
 {
     if (buf->is<ArrayBufferObject>())
         return buf->as<ArrayBufferObject>().wasmMaxSize();
-
-    return Some(buf->as<SharedArrayBufferObject>().byteLength());
+    return buf->as<SharedArrayBufferObject>().wasmMaxSize();
 }
 
 /* static */ bool
 ArrayBufferObject::wasmGrowToSizeInPlace(uint32_t newSize,
                                          HandleArrayBufferObject oldBuf,
                                          MutableHandleArrayBufferObject newBuf,
                                          JSContext* cx)
 {
     // On failure, do not throw and ensure that the original buffer is
     // unmodified and valid. After WasmArrayRawBuffer::growToSizeInPlace(), the
     // wasm-visible length of the buffer has been increased so it must be the
     // last fallible operation.
 
-    // byteLength can be at most INT32_MAX. Note: if this hard limit changes,
-    // update the clamping behavior in wasm::DecodeMemoryLimits and remove this
-    // comment as well as the one in wasmMovingGrowToSize.
-    if (newSize > INT32_MAX)
+    if (newSize > ArrayBufferObject::MaxBufferByteLength)
         return false;
 
     newBuf.set(ArrayBufferObject::createEmpty(cx));
     if (!newBuf) {
         cx->clearPendingException();
         return false;
     }
 
@@ -1066,19 +1083,17 @@ ArrayBufferObject::wasmGrowToSizeInPlace
 ArrayBufferObject::wasmMovingGrowToSize(uint32_t newSize,
                                         HandleArrayBufferObject oldBuf,
                                         MutableHandleArrayBufferObject newBuf,
                                         JSContext* cx)
 {
     // On failure, do not throw and ensure that the original buffer is
     // unmodified and valid.
 
-    // byteLength can be at most INT32_MAX.
-    // See comment in wasmGrowToSizeInPlace about wasm::DecodeMemoryLimits.
-    if (newSize > INT32_MAX)
+    if (newSize > ArrayBufferObject::MaxBufferByteLength)
         return false;
 
     if (newSize <= oldBuf->wasmBoundsCheckLimit() ||
         oldBuf->contents().wasmBuffer()->extendMappedSize(newSize))
     {
         return wasmGrowToSizeInPlace(newSize, oldBuf, newBuf, cx);
     }
 
@@ -1099,27 +1114,25 @@ ArrayBufferObject::wasmMovingGrowToSize(
     return true;
 }
 
 uint32_t
 ArrayBufferObject::wasmBoundsCheckLimit() const
 {
     if (isWasm())
         return contents().wasmBuffer()->boundsCheckLimit();
-    else
-        return byteLength();
+    return byteLength();
 }
 
 uint32_t
 ArrayBufferObjectMaybeShared::wasmBoundsCheckLimit() const
 {
     if (is<ArrayBufferObject>())
         return as<ArrayBufferObject>().wasmBoundsCheckLimit();
-
-    return as<SharedArrayBufferObject>().byteLength();
+    return as<SharedArrayBufferObject>().wasmBoundsCheckLimit();
 }
 #endif
 
 uint32_t
 ArrayBufferObject::flags() const
 {
     return uint32_t(getSlot(FLAGS_SLOT).toInt32());
 }
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -10,16 +10,17 @@
 #include "mozilla/Maybe.h"
 
 #include "jsobj.h"
 
 #include "builtin/TypedObjectConstants.h"
 #include "js/GCHashTable.h"
 #include "vm/Runtime.h"
 #include "vm/SharedMem.h"
+#include "wasm/WasmTypes.h"
 
 typedef struct JSProperty JSProperty;
 
 namespace js {
 
 class ArrayBufferViewObject;
 class WasmArrayRawBuffer;
 
@@ -101,16 +102,17 @@ int32_t LiveMappedBufferCount();
 
 class ArrayBufferObjectMaybeShared;
 
 uint32_t AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared* buf);
 mozilla::Maybe<uint32_t> WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf);
 size_t WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf);
 bool WasmArrayBufferGrowForWasm(ArrayBufferObjectMaybeShared* buf, uint32_t delta);
 bool AnyArrayBufferIsPreparedForAsmJS(const ArrayBufferObjectMaybeShared* buf);
+bool AnyArrayBufferIsWasm(const ArrayBufferObjectMaybeShared* buf);
 ArrayBufferObjectMaybeShared& AsAnyArrayBuffer(HandleValue val);
 
 class ArrayBufferObjectMaybeShared : public NativeObject
 {
   public:
     uint32_t byteLength() {
         return AnyArrayBufferByteLength(this);
     }
@@ -131,16 +133,20 @@ class ArrayBufferObjectMaybeShared : pub
     }
 #ifndef WASM_HUGE_MEMORY
     uint32_t wasmBoundsCheckLimit() const;
 #endif
 
     bool isPreparedForAsmJS() const {
         return AnyArrayBufferIsPreparedForAsmJS(this);
     }
+
+    bool isWasm() const {
+        return AnyArrayBufferIsWasm(this);
+    }
 };
 
 typedef Rooted<ArrayBufferObjectMaybeShared*> RootedArrayBufferObjectMaybeShared;
 typedef Handle<ArrayBufferObjectMaybeShared*> HandleArrayBufferObjectMaybeShared;
 typedef MutableHandle<ArrayBufferObjectMaybeShared*> MutableHandleArrayBufferObjectMaybeShared;
 
 /*
  * ArrayBufferObject
@@ -168,16 +174,21 @@ class ArrayBufferObject : public ArrayBu
     static const uint8_t RESERVED_SLOTS = 4;
 
     static const size_t ARRAY_BUFFER_ALIGNMENT = 8;
 
     static_assert(FLAGS_SLOT == JS_ARRAYBUFFER_FLAGS_SLOT,
                   "self-hosted code with burned-in constants must get the "
                   "right flags slot");
 
+    // The length of an ArrayBuffer or SharedArrayBuffer can be at most
+    // INT32_MAX, and much code must change if this changes.
+
+    static const size_t MaxBufferByteLength = INT32_MAX;
+
   public:
 
     enum OwnsState {
         DoesntOwnData = 0,
         OwnsData = 1,
     };
 
     enum BufferKind {
@@ -353,20 +364,19 @@ class ArrayBufferObject : public ArrayBu
     BufferKind bufferKind() const { return BufferKind(flags() & BUFFER_KIND_MASK); }
     bool isPlain() const { return bufferKind() == PLAIN; }
     bool isWasm() const { return bufferKind() == WASM; }
     bool isMapped() const { return bufferKind() == MAPPED; }
     bool isDetached() const { return flags() & DETACHED; }
     bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
 
     // WebAssembly support:
-    static ArrayBufferObject* createForWasm(JSContext* cx, uint32_t initialSize,
-                                            const mozilla::Maybe<uint32_t>& maxSize);
     static MOZ_MUST_USE bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                              bool needGuard);
+    void initializeRawBuffer(JSContext* cx, WasmArrayRawBuffer* buffer, uint32_t byteLength);
     size_t wasmMappedSize() const;
     mozilla::Maybe<uint32_t> wasmMaxSize() const;
     static MOZ_MUST_USE bool wasmGrowToSizeInPlace(uint32_t newSize,
                                                    Handle<ArrayBufferObject*> oldBuf,
                                                    MutableHandle<ArrayBufferObject*> newBuf,
                                                    JSContext* cx);
 #ifndef WASM_HUGE_MEMORY
     static MOZ_MUST_USE bool wasmMovingGrowToSize(uint32_t newSize,
@@ -429,16 +439,19 @@ class ArrayBufferObject : public ArrayBu
         setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
     }
 };
 
 typedef Rooted<ArrayBufferObject*> RootedArrayBufferObject;
 typedef Handle<ArrayBufferObject*> HandleArrayBufferObject;
 typedef MutableHandle<ArrayBufferObject*> MutableHandleArrayBufferObject;
 
+bool CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
+                      MutableHandleArrayBufferObjectMaybeShared buffer);
+
 /*
  * ArrayBufferViewObject
  *
  * Common definitions shared by all array buffer views.
  */
 
 class ArrayBufferViewObject : public JSObject
 {
--- a/js/src/vm/MutexIDs.h
+++ b/js/src/vm/MutexIDs.h
@@ -44,16 +44,17 @@
   _(OffThreadPromiseState,       500) \
   _(BufferStreamState,           500) \
   _(WasmCodeProfilingLabels,     500) \
   _(WasmModuleTieringLock,       500) \
   _(WasmCompileTaskState,        500) \
   _(WasmCodeStreamEnd,           500) \
   _(WasmTailBytesPtr,            500) \
   _(WasmStreamStatus,            500) \
+  _(SharedArrayGrow,             500)    \
                                       \
   _(ThreadId,                    600) \
   _(WasmCodeSegmentMap,          600) \
   _(TraceLoggerGraphState,       600) \
   _(VTuneLock,                   600)
 
 namespace js {
 namespace mutexid {
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -2,82 +2,165 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/SharedArrayObject.h"
 
 #include "mozilla/Atomics.h"
+#include "mozilla/CheckedInt.h"
 
 #include "jsfriendapi.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "jit/AtomicOperations.h"
 #include "vm/SharedMem.h"
 #include "wasm/AsmJS.h"
+#include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTypes.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
+using mozilla::Some;
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::CheckedInt;
+
 using namespace js;
 
-// Since this SharedArrayBuffer will likely be used for asm.js code, prepare it
-// for asm.js by mapping the 4gb protected zone described in WasmTypes.h.
-// Since we want to put the SharedArrayBuffer header immediately before the
-// heap but keep the heap page-aligned, allocate an extra page before the heap.
-static uint64_t
-SharedArrayMappedSize(uint32_t allocSize)
+// allocSize does not include the header page.
+static size_t
+SharedArrayMappedSizeForAsmJS(size_t allocSize)
 {
     MOZ_RELEASE_ASSERT(sizeof(SharedArrayRawBuffer) < gc::SystemPageSize());
 #ifdef WASM_HUGE_MEMORY
-    return wasm::HugeMappedSize + gc::SystemPageSize();
+    // Since this SharedArrayBuffer will likely be used for asm.js code, prepare
+    // it for asm.js by mapping the 4gb protected zone described in WasmTypes.h.
+    return wasm::HugeMappedSize;
 #else
+    MOZ_ASSERT(allocSize % gc::SystemPageSize() == 0);
     return allocSize + wasm::GuardSize;
 #endif
 }
 
+static size_t
+SharedArrayMappedSizeForWasm(size_t declaredMaxSize)
+{
+#ifdef WASM_HUGE_MEMORY
+    return wasm::HugeMappedSize;
+#else
+    return wasm::ComputeMappedSize(declaredMaxSize);
+#endif
+}
+
 static uint32_t
-SharedArrayAllocSize(uint32_t length)
+SharedArrayAccessibleSize(uint32_t length)
 {
-    return AlignBytes(length + gc::SystemPageSize(), gc::SystemPageSize());
+    return AlignBytes(length, gc::SystemPageSize());
 }
 
+// `max` must be something for wasm, nothing for other cases.
 SharedArrayRawBuffer*
-SharedArrayRawBuffer::New(uint32_t length)
+SharedArrayRawBuffer::Allocate(uint32_t length, const Maybe<uint32_t>& max)
 {
-    // The value (uint32_t)-1 is used as a signal in various places,
-    // so guard against it on principle.
-    MOZ_ASSERT(length != (uint32_t)-1);
+    MOZ_RELEASE_ASSERT(length <= ArrayBufferObject::MaxBufferByteLength);
 
-    // Add a page for the header and round to a page boundary.
-    uint32_t allocSize = SharedArrayAllocSize(length);
-    if (allocSize <= length)
+    // A buffer cannot be used for both asm.js and wasm at the same time.
+    bool preparedForWasm = max.isSome();
+    bool preparedForAsmJS = !preparedForWasm &&
+                            jit::JitOptions.asmJSAtomicsEnable &&
+                            IsValidAsmJSHeapLength(length);
+
+    uint32_t accessibleSize = SharedArrayAccessibleSize(length);
+    if (accessibleSize < length)
         return nullptr;
 
-    bool preparedForAsmJS = jit::JitOptions.asmJSAtomicsEnable && IsValidAsmJSHeapLength(length);
+    uint32_t maxSize = max.isSome() ? *max : accessibleSize;
 
-    void* p = nullptr;
-    if (preparedForAsmJS)
-        p = MapBufferMemory(SharedArrayMappedSize(allocSize), allocSize);
+    size_t mappedSize;
+    if (preparedForWasm)
+        mappedSize = SharedArrayMappedSizeForWasm(maxSize);
+    else if (preparedForAsmJS)
+        mappedSize = SharedArrayMappedSizeForAsmJS(accessibleSize);
     else
-        p = MapBufferMemory(allocSize, allocSize);
+        mappedSize = accessibleSize;
+
+    uint64_t mappedSizeWithHeader = mappedSize + gc::SystemPageSize();
+    uint64_t accessibleSizeWithHeader = accessibleSize + gc::SystemPageSize();
+
+    void* p = MapBufferMemory(mappedSizeWithHeader, accessibleSizeWithHeader);
     if (!p)
         return nullptr;
 
     uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
     uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
-    SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length, preparedForAsmJS);
+    SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer,
+                                                                   length,
+                                                                   maxSize,
+                                                                   mappedSize,
+                                                                   preparedForAsmJS,
+                                                                   preparedForWasm);
     MOZ_ASSERT(rawbuf->length_ == length); // Deallocation needs this
     return rawbuf;
 }
 
+#ifndef WASM_HUGE_MEMORY
+void
+SharedArrayRawBuffer::tryGrowMaxSizeInPlace(uint32_t deltaMaxSize)
+{
+    CheckedInt<uint32_t> newMaxSize = maxSize_;
+    newMaxSize += deltaMaxSize;
+    MOZ_ASSERT(newMaxSize.isValid());
+    MOZ_ASSERT(newMaxSize.value() % wasm::PageSize == 0);
+
+    size_t newMappedSize = SharedArrayMappedSizeForWasm(newMaxSize.value());
+    MOZ_ASSERT(mappedSize_ <= newMappedSize);
+    if (mappedSize_ == newMappedSize)
+        return;
+
+    if (!ExtendBufferMapping(basePointer(), mappedSize_, newMappedSize))
+        return;
+
+    mappedSize_ = newMappedSize;
+    maxSize_ = newMaxSize.value();
+}
+#endif
+
+bool
+SharedArrayRawBuffer::wasmGrowToSizeInPlace(const Lock&, uint32_t newLength)
+{
+    if (newLength > ArrayBufferObject::MaxBufferByteLength)
+        return false;
+
+    MOZ_ASSERT(newLength >= length_);
+
+    if (newLength == length_)
+        return true;
+
+    uint32_t delta = newLength - length_;
+    MOZ_ASSERT(delta % wasm::PageSize == 0);
+
+    uint8_t* dataEnd = dataPointerShared().unwrap(/* for resize */) + length_;
+    MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0);
+
+    // The ordering of committing memory and changing length does not matter
+    // since all clients take the lock.
+
+    if (!CommitBufferMemory(dataEnd, delta))
+        return false;
+
+    length_ = newLength;
+
+    return true;
+}
+
 bool
 SharedArrayRawBuffer::addReference()
 {
     MOZ_RELEASE_ASSERT(refcount_ > 0);
 
     // Be careful never to overflow the refcount field.
     for (;;) {
         uint32_t old_refcount = refcount_;
@@ -97,26 +180,20 @@ SharedArrayRawBuffer::dropReference()
     // reason we will catch the underflow here.
     MOZ_RELEASE_ASSERT(refcount_ > 0);
 
     // Drop the reference to the buffer.
     uint32_t new_refcount = --refcount_; // Atomic.
     if (new_refcount)
         return;
 
-    // If this was the final reference, release the buffer.
-    SharedMem<uint8_t*> p = dataPointerShared() - gc::SystemPageSize();
-    MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
+    size_t mappedSizeWithHeader = mappedSize_ + gc::SystemPageSize();
 
-    uint8_t* address = p.unwrap(/*safe - only reference*/);
-    uint32_t allocSize = SharedArrayAllocSize(length_);
-    if (preparedForAsmJS_)
-        UnmapBufferMemory(address, SharedArrayMappedSize(allocSize));
-    else
-        UnmapBufferMemory(address, allocSize);
+    // This was the final reference, so release the buffer.
+    UnmapBufferMemory(basePointer(), mappedSizeWithHeader);
 }
 
 
 MOZ_ALWAYS_INLINE bool
 SharedArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
     args.rval().setInt32(args.thisv().toObject().as<SharedArrayBufferObject>().byteLength());
@@ -165,51 +242,53 @@ SharedArrayBufferObject::class_construct
         return false;
     args.rval().setObject(*bufobj);
     return true;
 }
 
 SharedArrayBufferObject*
 SharedArrayBufferObject::New(JSContext* cx, uint32_t length, HandleObject proto)
 {
-    SharedArrayRawBuffer* buffer = SharedArrayRawBuffer::New(length);
+    SharedArrayRawBuffer* buffer = SharedArrayRawBuffer::Allocate(length, Nothing());
     if (!buffer)
         return nullptr;
 
-    SharedArrayBufferObject* obj = New(cx, buffer, proto);
+    SharedArrayBufferObject* obj = New(cx, buffer, length, proto);
     if (!obj) {
         buffer->dropReference();
         return nullptr;
     }
 
     return obj;
 }
 
 SharedArrayBufferObject*
-SharedArrayBufferObject::New(JSContext* cx, SharedArrayRawBuffer* buffer, HandleObject proto)
+SharedArrayBufferObject::New(JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t length,
+                             HandleObject proto)
 {
     MOZ_ASSERT(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
 
     AutoSetNewObjectMetadata metadata(cx);
     Rooted<SharedArrayBufferObject*> obj(cx,
         NewObjectWithClassProto<SharedArrayBufferObject>(cx, proto));
     if (!obj)
         return nullptr;
 
     MOZ_ASSERT(obj->getClass() == &class_);
 
-    obj->acceptRawBuffer(buffer);
+    obj->acceptRawBuffer(buffer, length);
 
     return obj;
 }
 
 void
-SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer* buffer)
+SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer* buffer, uint32_t length)
 {
     setReservedSlot(RAWBUF_SLOT, PrivateValue(buffer));
+    setReservedSlot(LENGTH_SLOT, PrivateUint32Value(length));
 }
 
 void
 SharedArrayBufferObject::dropRawBuffer()
 {
     setReservedSlot(RAWBUF_SLOT, UndefinedValue());
 }
 
@@ -232,16 +311,26 @@ SharedArrayBufferObject::Finalize(FreeOp
     // which causes a SharedArrayRawBuffer to never be attached.
     Value v = buf.getReservedSlot(RAWBUF_SLOT);
     if (!v.isUndefined()) {
         buf.rawBufferObject()->dropReference();
         buf.dropRawBuffer();
     }
 }
 
+#ifndef WASM_HUGE_MEMORY
+uint32_t
+SharedArrayBufferObject::wasmBoundsCheckLimit() const
+{
+    if (isWasm())
+        return rawBufferObject()->boundsCheckLimit();
+    return byteLength();
+}
+#endif
+
 /* static */ void
 SharedArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
                                                 JS::ClassInfo* info)
 {
     // Divide the buffer size by the refcount to get the fraction of the buffer
     // owned by this thread. It's conceivable that the refcount might change in
     // the middle of memory reporting, in which case the amount reported for
     // some threads might be to high (if the refcount goes up) or too low (if
@@ -262,16 +351,44 @@ SharedArrayBufferObject::copyData(Handle
     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
 
     jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared() + toIndex,
                                               fromBuffer->dataPointerShared() + fromIndex,
                                               count);
 }
 
+SharedArrayBufferObject*
+SharedArrayBufferObject::createEmpty(JSContext* cx)
+{
+    MOZ_ASSERT(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
+
+    AutoSetNewObjectMetadata metadata(cx);
+    Rooted<SharedArrayBufferObject*> obj(cx,
+        NewObjectWithClassProto<SharedArrayBufferObject>(cx, nullptr));
+    if (!obj)
+        return nullptr;
+
+    MOZ_ASSERT(obj->getClass() == &class_);
+
+    obj->setReservedSlot(RAWBUF_SLOT, UndefinedValue());
+    obj->setReservedSlot(LENGTH_SLOT, UndefinedValue());
+
+    return obj;
+}
+
+void
+SharedArrayBufferObject::initializeRawBuffer(JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t length)
+{
+    MOZ_ASSERT(getReservedSlot(RAWBUF_SLOT).isUndefined());
+    MOZ_ASSERT(getReservedSlot(LENGTH_SLOT).isUndefined());
+
+    acceptRawBuffer(buffer, length);
+}
+
 static JSObject*
 CreateSharedArrayBufferPrototype(JSContext* cx, JSProtoKey key)
 {
     return GlobalObject::createBlankPrototype(cx, cx->global(),
                                               &SharedArrayBufferObject::protoClass_);
 }
 
 static const ClassOps SharedArrayBufferObjectClassOps = {
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -35,40 +35,78 @@ class FutexWaiter;
  *
  * Observe that if we want to map the data array on a specific address, such
  * as absolute zero (bug 1056027), then the SharedArrayRawBuffer cannot be
  * prefixed to the data array, it has to be a separate object, also in
  * shared memory.  (That would get rid of ~4KB of waste, as well.)  Very little
  * else would have to change throughout the engine, the SARB would point to
  * the data array using a constant pointer, instead of computing its
  * address.
+ *
+ * If preparedForAsmJS_ is true then length_ never changes.
+ *
+ * If preparedForWasm_ is true then length_ can change following initialization;
+ * it may grow toward maxSize_.  See extensive comments above WasmArrayRawBuffer
+ * in ArrayBufferObject.cpp.
+ *
+ * length_ only grows when the lock is held.
  */
 class SharedArrayRawBuffer
 {
   private:
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
+    Mutex    lock_;
     uint32_t length_;
-    bool preparedForAsmJS_;
+    uint32_t maxSize_;
+    size_t   mappedSize_;         // Does not include the page for the header
+    bool     preparedForAsmJS_;
+    bool     preparedForWasm_;
 
     // A list of structures representing tasks waiting on some
     // location within this buffer.
     FutexWaiter* waiters_;
 
+    uint8_t* basePointer() {
+        SharedMem<uint8_t*> p = dataPointerShared() - gc::SystemPageSize();
+        MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
+        return p.unwrap(/* we trust you won't abuse it */);
+    }
+
   protected:
-    SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, bool preparedForAsmJS)
+    SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, uint32_t maxSize, size_t mappedSize,
+                         bool preparedForAsmJS, bool preparedForWasm)
       : refcount_(1),
+        lock_(mutexid::SharedArrayGrow),
         length_(length),
+        maxSize_(maxSize),
+        mappedSize_(mappedSize),
         preparedForAsmJS_(preparedForAsmJS),
+        preparedForWasm_(preparedForWasm),
         waiters_(nullptr)
     {
         MOZ_ASSERT(buffer == dataPointerShared());
     }
 
   public:
-    static SharedArrayRawBuffer* New(uint32_t length);
+    class Lock;
+    friend class Lock;
+
+    class MOZ_STACK_CLASS Lock {
+        SharedArrayRawBuffer* buf;
+      public:
+        explicit Lock(SharedArrayRawBuffer* buf) : buf(buf) {
+            buf->lock_.lock();
+        }
+        ~Lock() {
+            buf->lock_.unlock();
+        }
+    };
+
+    // max must be Something for wasm, Nothing for other uses
+    static SharedArrayRawBuffer* Allocate(uint32_t initial, const mozilla::Maybe<uint32_t>& max);
 
     // This may be called from multiple threads.  The caller must take
     // care of mutual exclusion.
     FutexWaiter* waiters() const {
         return waiters_;
     }
 
     // This may be called from multiple threads.  The caller must take
@@ -77,24 +115,48 @@ class SharedArrayRawBuffer
         waiters_ = waiters;
     }
 
     SharedMem<uint8_t*> dataPointerShared() const {
         uint8_t* ptr = reinterpret_cast<uint8_t*>(const_cast<SharedArrayRawBuffer*>(this));
         return SharedMem<uint8_t*>::shared(ptr + sizeof(SharedArrayRawBuffer));
     }
 
-    uint32_t byteLength() const {
+    uint32_t byteLength(const Lock&) const {
         return length_;
     }
 
+    uint32_t maxSize() const {
+        return maxSize_;
+    }
+
+    size_t mappedSize() const {
+        return mappedSize_;
+    }
+
+#ifndef WASM_HUGE_MEMORY
+    uint32_t boundsCheckLimit() const {
+        return mappedSize_ - wasm::GuardSize;
+    }
+#endif
+
     bool isPreparedForAsmJS() const {
         return preparedForAsmJS_;
     }
 
+    bool isWasm() const {
+        return preparedForWasm_;
+    }
+
+#ifndef WASM_HUGE_MEMORY
+    void tryGrowMaxSizeInPlace(uint32_t deltaMaxSize);
+#endif
+
+    bool wasmGrowToSizeInPlace(const Lock&, uint32_t newLength);
+
     uint32_t refcount() const { return refcount_; }
 
     MOZ_MUST_USE bool addReference();
     void dropReference();
 
     static int32_t liveBuffers();
 };
 
@@ -121,33 +183,41 @@ class SharedArrayBufferObject : public A
 {
     static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
 
   public:
     // RAWBUF_SLOT holds a pointer (as "private" data) to the
     // SharedArrayRawBuffer object, which is manually managed storage.
     static const uint8_t RAWBUF_SLOT = 0;
 
-    static const uint8_t RESERVED_SLOTS = 1;
+    // LENGTH_SLOT holds the length of the underlying buffer as it was when this
+    // object was created.  For JS and AsmJS use cases this is the same length
+    // as the buffer, but for Wasm the buffer can grow, and the buffer's length
+    // may be greater than the object's length.
+    static const uint8_t LENGTH_SLOT = 1;
+
+    static const uint8_t RESERVED_SLOTS = 2;
 
     static const Class class_;
     static const Class protoClass_;
 
     static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
 
     static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
 
     // Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
     static SharedArrayBufferObject* New(JSContext* cx,
                                         uint32_t length,
                                         HandleObject proto = nullptr);
 
-    // Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
+    // Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer,
+    // recording the given length in the SharedArrayBufferObject.
     static SharedArrayBufferObject* New(JSContext* cx,
                                         SharedArrayRawBuffer* buffer,
+                                        uint32_t length,
                                         HandleObject proto = nullptr);
 
     static void Finalize(FreeOp* fop, JSObject* obj);
 
     static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
                                        JS::ClassInfo* info);
 
     static void copyData(Handle<SharedArrayBufferObject*> toBuffer, uint32_t toIndex,
@@ -161,28 +231,52 @@ class SharedArrayBufferObject : public A
     uintptr_t globalID() const {
         // The buffer address is good enough as an ID provided the memory is not shared
         // between processes or, if it is, it is mapped to the same address in every
         // process.  (At the moment, shared memory cannot be shared between processes.)
         return dataPointerShared().asValue();
     }
 
     uint32_t byteLength() const {
-        return rawBufferObject()->byteLength();
+        return getReservedSlot(LENGTH_SLOT).toPrivateUint32();
     }
+
     bool isPreparedForAsmJS() const {
         return rawBufferObject()->isPreparedForAsmJS();
     }
-
+    bool isWasm() const {
+        return rawBufferObject()->isWasm();
+    }
     SharedMem<uint8_t*> dataPointerShared() const {
         return rawBufferObject()->dataPointerShared();
     }
 
+    // WebAssembly support:
+
+    // Create a SharedArrayBufferObject without an attached SARB.  This is a
+    // valid object only if a SARB is then attached with initializeRawBuffer().
+    static SharedArrayBufferObject* createEmpty(JSContext* cx);
+
+    // Install the buffer if the object was created by createEmpty().
+    void initializeRawBuffer(JSContext* cx, SharedArrayRawBuffer* buffer, uint32_t length);
+
+    mozilla::Maybe<uint32_t> wasmMaxSize() const {
+        return mozilla::Some(rawBufferObject()->maxSize());
+    }
+
+    size_t wasmMappedSize() const {
+        return rawBufferObject()->mappedSize();
+    }
+
+#ifndef WASM_HUGE_MEMORY
+    uint32_t wasmBoundsCheckLimit() const;
+#endif
+
 private:
-    void acceptRawBuffer(SharedArrayRawBuffer* buffer);
+    void acceptRawBuffer(SharedArrayRawBuffer* buffer, uint32_t length);
     void dropRawBuffer();
 };
 
 bool IsSharedArrayBuffer(HandleValue v);
 bool IsSharedArrayBuffer(HandleObject o);
 bool IsSharedArrayBuffer(JSObject* o);
 
 SharedArrayBufferObject& AsSharedArrayBuffer(HandleObject o);
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1986,18 +1986,18 @@ JSStructuredCloneReader::readSharedArray
 
     // The new object will have a new reference to the rawbuf.
 
     if (!rawbuf->addReference()) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
         return false;
     }
 
-    JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf);
-
+    SharedArrayRawBuffer::Lock l(rawbuf);
+    JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf, rawbuf->byteLength(l));
     if (!obj) {
         rawbuf->dropReference();
         return false;
     }
 
     vp.setObject(*obj);
     return true;
 }
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -2064,17 +2064,17 @@ class MOZ_STACK_CLASS ModuleValidator
         g.pod.u.constant.value_ = constant;
         g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
         return asmJSMetadata_->asmJSGlobals.append(Move(g));
     }
     bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func,
                                    PropertyName* field)
     {
         if (!JitOptions.asmJSAtomicsEnable)
-            return failCurrentOffset("asm.js Atomics only enabled in wasm test mode");
+            return failCurrentOffset("asm.js Atomics only enabled when asmjs.atomics.enable is set");
 
         atomicsPresent_ = true;
 
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
         Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
@@ -8018,18 +8018,23 @@ CheckBuffer(JSContext* cx, const AsmJSMe
         bool needGuard = true;
 #else
         bool needGuard = metadata.usesSimd;
 #endif
         Rooted<ArrayBufferObject*> arrayBuffer(cx, &buffer->as<ArrayBufferObject>());
         if (!ArrayBufferObject::prepareForAsmJS(cx, arrayBuffer, needGuard))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
     } else {
-        if (!buffer->as<SharedArrayBufferObject>().isPreparedForAsmJS())
-            return LinkFail(cx, "SharedArrayBuffer must be created with wasm test mode enabled");
+        if (!buffer->as<SharedArrayBufferObject>().isPreparedForAsmJS()) {
+            if (buffer->as<SharedArrayBufferObject>().isWasm())
+                return LinkFail(cx, "SharedArrayBuffer created for Wasm cannot be used for asm.js");
+            if (!jit::JitOptions.asmJSAtomicsEnable)
+                return LinkFail(cx, "Can link with SharedArrayBuffer only when asmjs.atomics.enable is set");
+            return LinkFail(cx, "Unable to prepare SharedArrayBuffer for asm.js use");
+        }
     }
 
     MOZ_ASSERT(buffer->isPreparedForAsmJS());
     return true;
 }
 
 static bool
 GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -299,17 +299,17 @@ Instance::growMemory_i32(Instance* insta
                        instance->memory_->buffer().dataPointerEither());
 
     return ret;
 }
 
 /* static */ uint32_t
 Instance::currentMemory_i32(Instance* instance)
 {
-    uint32_t byteLength = instance->memoryLength();
+    uint32_t byteLength = instance->memory()->volatileMemoryLength();
     MOZ_ASSERT(byteLength % wasm::PageSize == 0);
     return byteLength / wasm::PageSize;
 }
 
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    SharedCode code,
                    UniqueDebugState debug,
@@ -471,31 +471,33 @@ Instance::~Instance()
 }
 
 size_t
 Instance::memoryMappedSize() const
 {
     return memory_->buffer().wasmMappedSize();
 }
 
+#ifdef JS_SIMULATOR
 bool
 Instance::memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const
 {
     MOZ_ASSERT(numBytes > 0);
 
     if (!metadata().usesMemory())
         return false;
 
     uint8_t* base = memoryBase().unwrap(/* comparison */);
     if (addr < base)
         return false;
 
     size_t lastByteOffset = addr - base + (numBytes - 1);
-    return lastByteOffset >= memoryLength() && lastByteOffset < memoryMappedSize();
+    return lastByteOffset >= memory()->volatileMemoryLength() && lastByteOffset < memoryMappedSize();
 }
+#endif
 
 void
 Instance::tracePrivate(JSTracer* trc)
 {
     // This method is only called from WasmInstanceObject so the only reason why
     // TraceEdge is called is so that the pointer can be updated during a moving
     // GC. TraceWeakEdge may sound better, but it is less efficient given that
     // we know object_ is already marked.
@@ -533,20 +535,21 @@ Instance::memory() const
 SharedMem<uint8_t*>
 Instance::memoryBase() const
 {
     MOZ_ASSERT(metadata().usesMemory());
     MOZ_ASSERT(tlsData()->memoryBase == memory_->buffer().dataPointerEither());
     return memory_->buffer().dataPointerEither();
 }
 
-size_t
-Instance::memoryLength() const
+SharedArrayRawBuffer*
+Instance::sharedMemoryBuffer() const
 {
-    return memory_->buffer().byteLength();
+    MOZ_ASSERT(memory_->isShared());
+    return memory_->sharedArrayRawBuffer();
 }
 
 WasmInstanceObject*
 Instance::objectUnbarriered() const
 {
     return object_.unbarrieredGet();
 }
 
@@ -760,16 +763,18 @@ Instance::ensureProfilingLabels(bool pro
 {
     return code_->ensureProfilingLabels(debug_->maybeBytecode(), profilingEnabled);
 }
 
 void
 Instance::onMovingGrowMemory(uint8_t* prevMemoryBase)
 {
     MOZ_ASSERT(!isAsmJS());
+    MOZ_ASSERT(!memory_->isShared());
+
     ArrayBufferObject& buffer = memory_->buffer().as<ArrayBufferObject>();
     tlsData()->memoryBase = buffer.dataPointer();
 #ifndef WASM_HUGE_MEMORY
     tlsData()->boundsCheckLimit = buffer.wasmBoundsCheckLimit();
 #endif
 }
 
 void
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -116,19 +116,21 @@ class Instance
     const GlobalSegment& globalSegment() const { return *globals_; }
     uint8_t* codeBase(Tier t) const { return code_->segment(t).base(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     bool isAsmJS() const { return metadata().isAsmJS(); }
     const SharedTableVector& tables() const { return tables_; }
     SharedMem<uint8_t*> memoryBase() const;
     WasmMemoryObject* memory() const;
-    size_t memoryLength() const;
     size_t memoryMappedSize() const;
+    SharedArrayRawBuffer* sharedMemoryBuffer() const; // never null
+#ifdef JS_SIMULATOR
     bool memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const;
+#endif
     TlsData* tlsData() const { return globals_->tlsData(); }
 
     static size_t offsetOfJSJitArgsRectifier() { return offsetof(Instance, jsJitArgsRectifier_); }
 
     // This method returns a pointer to the GC object that owns this Instance.
     // Instances may be reached via weak edges (e.g., Compartment::instances_)
     // so this perform a read-barrier on the returned object unless the barrier
     // is explicitly waived.
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1284,19 +1284,18 @@ WasmMemoryObject::construct(JSContext* c
     {
         return false;
     }
 
     limits.initial *= PageSize;
     if (limits.maximum)
         limits.maximum = Some(*limits.maximum * PageSize);
 
-    RootedArrayBufferObject buffer(cx,
-        ArrayBufferObject::createForWasm(cx, limits.initial, limits.maximum));
-    if (!buffer)
+    RootedArrayBufferObjectMaybeShared buffer(cx);
+    if (!CreateWasmBuffer(cx, limits, &buffer))
         return false;
 
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
     RootedWasmMemoryObject memoryObj(cx, WasmMemoryObject::create(cx, buffer, proto));
     if (!memoryObj)
         return false;
 
     args.rval().setObject(*memoryObj);
@@ -1307,17 +1306,40 @@ static bool
 IsMemory(HandleValue v)
 {
     return v.isObject() && v.toObject().is<WasmMemoryObject>();
 }
 
 /* static */ bool
 WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args)
 {
-    args.rval().setObject(args.thisv().toObject().as<WasmMemoryObject>().buffer());
+    RootedWasmMemoryObject memoryObj(cx, &args.thisv().toObject().as<WasmMemoryObject>());
+    RootedArrayBufferObjectMaybeShared buffer(cx, &memoryObj->buffer());
+
+    if (memoryObj->isShared()) {
+        uint32_t memoryLength = memoryObj->volatileMemoryLength();
+        MOZ_ASSERT(memoryLength >= buffer->byteLength());
+
+        if (memoryLength > buffer->byteLength()) {
+            RootedSharedArrayBufferObject newBuffer(
+                cx, SharedArrayBufferObject::New(cx, memoryObj->sharedArrayRawBuffer(), memoryLength));
+            if (!newBuffer)
+                return false;
+            // OK to addReference after we try to allocate because the memoryObj
+            // keeps the rawBuffer alive.
+            if (!memoryObj->sharedArrayRawBuffer()->addReference()) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
+                return false;
+            }
+            buffer = newBuffer;
+            memoryObj->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuffer));
+        }
+    }
+
+    args.rval().setObject(*buffer);
     return true;
 }
 
 /* static */ bool
 WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
@@ -1366,16 +1388,39 @@ const JSFunctionSpec WasmMemoryObject::s
 { JS_FS_END };
 
 ArrayBufferObjectMaybeShared&
 WasmMemoryObject::buffer() const
 {
     return getReservedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObjectMaybeShared>();
 }
 
+SharedArrayRawBuffer*
+WasmMemoryObject::sharedArrayRawBuffer() const
+{
+    MOZ_ASSERT(isShared());
+    return buffer().as<SharedArrayBufferObject>().rawBufferObject();
+}
+
+uint32_t
+WasmMemoryObject::volatileMemoryLength() const
+{
+    if (isShared()) {
+        SharedArrayRawBuffer::Lock lock(sharedArrayRawBuffer());
+        return sharedArrayRawBuffer()->byteLength(lock);
+    }
+    return buffer().byteLength();
+}
+
+bool
+WasmMemoryObject::isShared() const
+{
+    return buffer().is<SharedArrayBufferObject>();
+}
+
 bool
 WasmMemoryObject::hasObservers() const
 {
     return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
 }
 
 WasmMemoryObject::InstanceSet&
 WasmMemoryObject::observers() const
@@ -1423,18 +1468,48 @@ WasmMemoryObject::addMovingGrowObserver(
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 /* static */ uint32_t
+WasmMemoryObject::growShared(HandleWasmMemoryObject memory, uint32_t delta, JSContext* cx)
+{
+    SharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer();
+    SharedArrayRawBuffer::Lock lock(rawBuf);
+
+    MOZ_ASSERT(rawBuf->byteLength(lock) % PageSize == 0);
+    uint32_t oldNumPages = rawBuf->byteLength(lock) / PageSize;
+
+    CheckedInt<uint32_t> newSize = oldNumPages;
+    newSize += delta;
+    newSize *= PageSize;
+    if (!newSize.isValid())
+        return -1;
+
+    if (newSize.value() > rawBuf->maxSize())
+        return -1;
+
+    if (!rawBuf->wasmGrowToSizeInPlace(lock, newSize.value()))
+        return -1;
+
+    // New buffer objects will be created lazily in all agents (including in
+    // this agent) by bufferGetterImpl, above, so no more work to do here.
+
+    return oldNumPages;
+}
+
+/* static */ uint32_t
 WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta, JSContext* cx)
 {
+    if (memory->isShared())
+        return growShared(memory, delta, cx);
+
     RootedArrayBufferObject oldBuf(cx, &memory->buffer().as<ArrayBufferObject>());
 
     MOZ_ASSERT(oldBuf->byteLength() % PageSize == 0);
     uint32_t oldNumPages = oldBuf->byteLength() / PageSize;
 
     CheckedInt<uint32_t> newSize = oldNumPages;
     newSize += delta;
     newSize *= PageSize;
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -187,16 +187,17 @@ class WasmMemoryObject : public NativeOb
     static const unsigned BUFFER_SLOT = 0;
     static const unsigned OBSERVERS_SLOT = 1;
     static const ClassOps classOps_;
     static void finalize(FreeOp* fop, JSObject* obj);
     static bool bufferGetterImpl(JSContext* cx, const CallArgs& args);
     static bool bufferGetter(JSContext* cx, unsigned argc, Value* vp);
     static bool growImpl(JSContext* cx, const CallArgs& args);
     static bool grow(JSContext* cx, unsigned argc, Value* vp);
+    static uint32_t growShared(HandleWasmMemoryObject memory, uint32_t delta, JSContext* cx);
 
     using InstanceSet = JS::WeakCache<GCHashSet<ReadBarrieredWasmInstanceObject,
                                                 MovableCellHasher<ReadBarrieredWasmInstanceObject>,
                                                 SystemAllocPolicy>>;
     bool hasObservers() const;
     InstanceSet& observers() const;
     InstanceSet* getOrCreateObservers(JSContext* cx);
 
@@ -206,19 +207,37 @@ class WasmMemoryObject : public NativeOb
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     static WasmMemoryObject* create(JSContext* cx,
                                     Handle<ArrayBufferObjectMaybeShared*> buffer,
                                     HandleObject proto);
+
+    // `buffer()` returns the current buffer object always.  If the buffer
+    // represents shared memory then `buffer().byteLength()` never changes, and
+    // in particular it may be a smaller value than that returned from
+    // `volatileMemoryLength()` below.
+    //
+    // Generally, you do not want to call `buffer().byteLength()`, but to call
+    // `volatileMemoryLength()`, instead.
     ArrayBufferObjectMaybeShared& buffer() const;
 
+    // The current length of the memory.  In the case of shared memory, the
+    // length can change at any time.  Also note that this will acquire a lock
+    // for shared memory, so do not call this from a signal handler.
+    uint32_t volatileMemoryLength() const;
+
+    bool isShared() const;
     bool movingGrowable() const;
+
+    // If isShared() is true then obtain the underlying buffer object.
+    SharedArrayRawBuffer* sharedArrayRawBuffer() const;
+
     bool addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance);
     static uint32_t grow(HandleWasmMemoryObject memory, uint32_t delta, JSContext* cx);
 };
 
 // The class of WebAssembly.Table. A WasmTableObject holds a refcount on a
 // wasm::Table, allowing a Table to be shared between multiple Instances
 // (eventually between multiple threads).
 
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -732,18 +732,18 @@ Module::initSegments(JSContext* cx,
         if (offset > tableLength || tableLength - offset < numElems) {
             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT,
                                      "elem", "table");
             return false;
         }
     }
 
     if (memoryObj) {
+        uint32_t memoryLength = memoryObj->volatileMemoryLength();
         for (const DataSegment& seg : dataSegments_) {
-            uint32_t memoryLength = memoryObj->buffer().byteLength();
             uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
 
             if (offset > memoryLength || memoryLength - offset < seg.length) {
                 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT,
                                          "data", "memory");
                 return false;
             }
         }
@@ -861,51 +861,71 @@ CheckLimits(JSContext* cx, uint32_t decl
     if ((actualMax && declaredMax && *actualMax > *declaredMax) || (!actualMax && declaredMax)) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_MAX, kind);
         return false;
     }
 
     return true;
 }
 
+static bool
+CheckSharing(JSContext* cx, bool declaredShared, bool isShared)
+{
+    if (declaredShared && !isShared) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_IMP_SHARED_REQD);
+        return false;
+    }
+
+    if (!declaredShared && isShared) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_IMP_SHARED_BANNED);
+        return false;
+    }
+
+    return true;
+}
+
 // asm.js module instantiation supplies its own buffer, but for wasm, create and
 // initialize the buffer if one is requested. Either way, the buffer is wrapped
 // in a WebAssembly.Memory object which is what the Instance stores.
 bool
 Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const
 {
     if (!metadata().usesMemory()) {
         MOZ_ASSERT(!memory);
         MOZ_ASSERT(dataSegments_.empty());
         return true;
     }
 
     uint32_t declaredMin = metadata().minMemoryLength;
     Maybe<uint32_t> declaredMax = metadata().maxMemoryLength;
+    bool declaredShared = metadata().memoryUsage == MemoryUsage::Shared;
 
     if (memory) {
-        ArrayBufferObjectMaybeShared& buffer = memory->buffer();
-        MOZ_ASSERT_IF(metadata().isAsmJS(), buffer.isPreparedForAsmJS());
-        MOZ_ASSERT_IF(!metadata().isAsmJS(), buffer.as<ArrayBufferObject>().isWasm());
+        MOZ_ASSERT_IF(metadata().isAsmJS(), memory->buffer().isPreparedForAsmJS());
+        MOZ_ASSERT_IF(!metadata().isAsmJS(), memory->buffer().isWasm());
 
-        if (!CheckLimits(cx, declaredMin, declaredMax, buffer.byteLength(), buffer.wasmMaxSize(),
-                         metadata().isAsmJS(), "Memory")) {
+        if (!CheckLimits(cx, declaredMin, declaredMax, memory->volatileMemoryLength(),
+                         memory->buffer().wasmMaxSize(), metadata().isAsmJS(), "Memory"))
+        {
             return false;
         }
+
+        if (!CheckSharing(cx, declaredShared, memory->isShared()))
+            return false;
     } else {
         MOZ_ASSERT(!metadata().isAsmJS());
-        MOZ_ASSERT(metadata().memoryUsage == MemoryUsage::Unshared);
 
-        RootedArrayBufferObjectMaybeShared buffer(cx,
-            ArrayBufferObject::createForWasm(cx, declaredMin, declaredMax));
-        if (!buffer)
+        RootedArrayBufferObjectMaybeShared buffer(cx);
+        Limits l(declaredMin,
+                 declaredMax,
+                 declaredShared ? Shareable::True : Shareable::False);
+        if (!CreateWasmBuffer(cx, l, &buffer))
             return false;
 
         RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
-
         memory.set(WasmMemoryObject::create(cx, buffer, proto));
         if (!memory)
             return false;
     }
 
     return true;
 }
 
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -808,16 +808,25 @@ HandleMemoryAccess(EMULATOR_CONTEXT* con
 
     if (memoryAccess->hasTrapOutOfLineCode()) {
         *ppc = memoryAccess->trapOutOfLineCode(segment->base());
         return;
     }
 
     MOZ_RELEASE_ASSERT(instance.isAsmJS());
 
+    // Asm.JS memory cannot grow or shrink - only wasm can grow or shrink it,
+    // and asm.js is not allowed to use wasm memory.  On this Asm.JS-only path
+    // we therefore need not worry about memory growing or shrinking while the
+    // signal handler is executing, and we can read the length without locking
+    // the memory.  Indeed, the buffer's byteLength always holds the correct
+    // value.
+
+    uint32_t memoryLength = instance.memory()->buffer().byteLength();
+
     // Disassemble the instruction which caused the trap so that we can extract
     // information about it and decide what to do.
     Disassembler::HeapAccess access;
     uint8_t* end = Disassembler::DisassembleHeapAccess(pc, &access);
     const Disassembler::ComplexAddress& address = access.address();
     MOZ_RELEASE_ASSERT(end > pc);
     MOZ_RELEASE_ASSERT(segment->containsCodePC(end));
 
@@ -848,17 +857,17 @@ HandleMemoryAccess(EMULATOR_CONTEXT* con
                        "Given faulting address does not appear to be within computed "
                        "faulting address range");
     MOZ_RELEASE_ASSERT(accessAddress >= instance.memoryBase(),
                        "Access begins outside the asm.js heap");
     MOZ_RELEASE_ASSERT(accessAddress + access.size() <= instance.memoryBase() +
                        instance.memoryMappedSize(),
                        "Access extends beyond the asm.js heap guard region");
     MOZ_RELEASE_ASSERT(accessAddress + access.size() > instance.memoryBase() +
-                       instance.memoryLength(),
+                       memoryLength,
                        "Computed access address is not actually out of bounds");
 
     // The basic sandbox model is that all heap accesses are a heap base
     // register plus an index, and the index is always computed with 32-bit
     // operations, so we know it can only be 4 GiB off of the heap base.
     //
     // However, we wish to support the optimization of folding immediates
     // and scaled indices into addresses, and any address arithmetic we fold
@@ -869,26 +878,26 @@ HandleMemoryAccess(EMULATOR_CONTEXT* con
     // wrapped address, and perform the load or store on it.
     //
     // Taking a signal is really slow, but in theory programs really shouldn't
     // be hitting this anyway.
     intptr_t unwrappedOffset = accessAddress - instance.memoryBase().unwrap(/* for value */);
     uint32_t wrappedOffset = uint32_t(unwrappedOffset);
     size_t size = access.size();
     MOZ_RELEASE_ASSERT(wrappedOffset + size > wrappedOffset);
-    bool inBounds = wrappedOffset + size < instance.memoryLength();
+    bool inBounds = wrappedOffset + size < memoryLength;
 
     if (inBounds) {
         // We now know that this is an access that is actually in bounds when
         // properly wrapped. Complete the load or store with the wrapped
         // address.
         SharedMem<uint8_t*> wrappedAddress = instance.memoryBase() + wrappedOffset;
         MOZ_RELEASE_ASSERT(wrappedAddress >= instance.memoryBase());
         MOZ_RELEASE_ASSERT(wrappedAddress + size > wrappedAddress);
-        MOZ_RELEASE_ASSERT(wrappedAddress + size <= instance.memoryBase() + instance.memoryLength());
+        MOZ_RELEASE_ASSERT(wrappedAddress + size <= instance.memoryBase() + memoryLength);
         switch (access.kind()) {
           case Disassembler::HeapAccess::Load:
             SetRegisterToLoadedValue(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
             break;
           case Disassembler::HeapAccess::LoadSext32:
             SetRegisterToLoadedValueSext32(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
             break;
           case Disassembler::HeapAccess::Store:
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -13,16 +13,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmTypes.h"
 
+#include "vm/ArrayBufferObject.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmSerialize.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
@@ -32,16 +33,21 @@ using mozilla::IsPowerOfTwo;
 
 // A sanity check.  We have only tested WASM_HUGE_MEMORY on x64, and only tested
 // x64 with WASM_HUGE_MEMORY.
 
 #if defined(WASM_HUGE_MEMORY) != defined(JS_CODEGEN_X64)
 #  error "Not an expected configuration"
 #endif
 
+// Another sanity check.
+
+static_assert(MaxMemoryInitialPages <= ArrayBufferObject::MaxBufferByteLength / PageSize,
+              "Memory sizing constraint");
+
 void
 Val::writePayload(uint8_t* dst) const
 {
     switch (type_) {
       case ValType::I32:
       case ValType::F32:
         memcpy(dst, &u.i32_, sizeof(u.i32_));
         return;