Bug 1389464 - Implement shared memory for WebAssembly. r=luke
authorLars T Hansen <lhansen@mozilla.com>
Fri, 22 Sep 2017 10:49:19 +0200
changeset 701918 5b8da4918cbafd4296c318f54fd6bbd2c9c453c3
parent 701917 d704d1e5b453f463329317dd36a3b3ad5b41ade0
child 701919 565ca96c41490c9367b476d41af9aa6e0b611bd9
push id90308
push userbmo:lhansen@mozilla.com
push dateWed, 22 Nov 2017 12:45:04 +0000
reviewersluke
bugs1389464
milestone59.0a1
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;