Bug 1389464 - Parse attributes for shared memory. r=luke
authorLars T Hansen <lhansen@mozilla.com>
Wed, 05 Jul 2017 13:43:31 -0700
changeset 701917 d704d1e5b453f463329317dd36a3b3ad5b41ade0
parent 701916 c8d3f0749e42b0f3891cd4d404f41f9640c7d2e5
child 701918 5b8da4918cbafd4296c318f54fd6bbd2c9c453c3
push id90308
push userbmo:lhansen@mozilla.com
push dateWed, 22 Nov 2017 12:45:04 +0000
reviewersluke
bugs1389464
milestone59.0a1
Bug 1389464 - Parse attributes for shared memory. r=luke
js/src/js.msg
js/src/wasm/WasmBinaryConstants.h
js/src/wasm/WasmBinaryToAST.cpp
js/src/wasm/WasmBinaryToText.cpp
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmTextToBinary.cpp
js/src/wasm/WasmTypes.h
js/src/wasm/WasmValidate.cpp
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -380,16 +380,17 @@ MSG_DEF(JSMSG_WASM_BAD_DESC_ARG,       1
 MSG_DEF(JSMSG_WASM_BAD_ELEMENT,        0, JSEXN_TYPEERR,     "\"element\" property of table descriptor must be \"anyfunc\"")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument must be an object")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD,   1, JSEXN_TYPEERR,     "import object field '{0}' is not an Object")
 MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE,    0, JSEXN_TYPEERR,     "can only assign WebAssembly exported functions to Table")
 MSG_DEF(JSMSG_WASM_BAD_I64_TYPE,       0, JSEXN_TYPEERR,     "cannot pass i64 to or from JS")
 MSG_DEF(JSMSG_WASM_NO_TRANSFER,        0, JSEXN_TYPEERR,     "cannot transfer WebAssembly/asm.js ArrayBuffer")
 MSG_DEF(JSMSG_WASM_STREAM_ERROR,       0, JSEXN_TYPEERR,     "stream error during WebAssembly compilation")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
+MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM,    0, JSEXN_TYPEERR,     "'shared' is true but maximum is not specified")
 
 // Proxy
 MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE,   2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
 MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
 MSG_DEF(JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler didn't return the target object's prototype")
 MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setPrototypeOf handler returned false")
 MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
 MSG_DEF(JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy setPrototypeOf handler returned true, even though the target's prototype is immutable because the target is non-extensible")
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -104,17 +104,25 @@ enum class DefinitionKind
 enum class GlobalTypeImmediate
 {
     IsMutable                            = 0x1,
     AllowedMask                          = 0x1
 };
 
 enum class MemoryTableFlags
 {
-    Default                              = 0x0
+    Default                              = 0x0,
+    HasMaximum                           = 0x1,
+    IsShared                             = 0x2
+};
+
+enum class MemoryMasks
+{
+    AllowUnshared                        = 0x1,
+    AllowShared                          = 0x3
 };
 
 enum class Op
 {
     // Control flow operators
     Unreachable                          = 0x00,
     Nop                                  = 0x01,
     Block                                = 0x02,
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -1544,20 +1544,21 @@ AstCreateImports(AstDecodeContext& c)
 {
     size_t lastFunc = 0;
     size_t lastGlobal = 0;
     size_t lastTable = 0;
     size_t lastMemory = 0;
 
     Maybe<Limits> memory;
     if (c.env().usesMemory()) {
-        Limits limits;
-        limits.initial = c.env().minMemoryLength;
-        limits.maximum = c.env().maxMemoryLength;
-        memory = Some(limits);
+        memory = Some(Limits(c.env().minMemoryLength,
+                             c.env().maxMemoryLength,
+                             c.env().memoryUsage == MemoryUsage::Shared
+                               ? Shareable::True
+                               : Shareable::False));
     }
 
     for (size_t importIndex = 0; importIndex < c.env().imports.length(); importIndex++) {
         const Import& import = c.env().imports[importIndex];
 
         AstName moduleName;
         if (!ToAstName(c, import.module.get(), &moduleName))
             return false;
@@ -1648,20 +1649,21 @@ AstCreateMemory(AstDecodeContext& c)
     bool importedMemory = !!c.module().memories().length();
     if (!c.env().usesMemory() || importedMemory)
         return true;
 
     AstName name;
     if (!GenerateName(c, AstName(u"memory"), c.module().memories().length(), &name))
         return false;
 
-    Limits limits;
-    limits.initial = c.env().minMemoryLength;
-    limits.maximum = c.env().maxMemoryLength;
-    return c.module().addMemory(name, limits);
+    return c.module().addMemory(name, Limits(c.env().minMemoryLength,
+                                             c.env().maxMemoryLength,
+                                             c.env().memoryUsage == MemoryUsage::Shared
+                                               ? Shareable::True
+                                               : Shareable::False));
 }
 
 static AstExpr*
 ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr)
 {
     switch (initExpr.kind()) {
       case InitExpr::Kind::Constant: {
         return new(c.lifo) AstConst(Val(initExpr.val()));
--- a/js/src/wasm/WasmBinaryToText.cpp
+++ b/js/src/wasm/WasmBinaryToText.cpp
@@ -1243,26 +1243,31 @@ RenderLimits(WasmRenderContext& c, const
     if (!RenderInt32(c, limits.initial))
         return false;
     if (limits.maximum) {
         if (!c.buffer.append(" "))
             return false;
         if (!RenderInt32(c, *limits.maximum))
             return false;
     }
+    if (limits.shared == Shareable::True) {
+        if (!c.buffer.append(" shared"))
+            return false;
+    }
     return true;
 }
 
 static bool
 RenderResizableTable(WasmRenderContext& c, const Limits& table)
 {
     if (!c.buffer.append("(table "))
         return false;
     if (!RenderLimits(c, table))
         return false;
+    MOZ_ASSERT(table.shared == Shareable::False);
     return c.buffer.append(" anyfunc)");
 }
 
 static bool
 RenderTableSection(WasmRenderContext& c, const AstModule& module)
 {
     if (!module.hasTable())
         return true;
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -317,17 +317,17 @@ EnforceRangeU32(JSContext* cx, HandleVal
 
     *u32 = uint32_t(x);
     MOZ_ASSERT(double(*u32) == x);
     return true;
 }
 
 static bool
 GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial, uint32_t maxMaximum,
-          const char* kind, Limits* limits)
+          const char* kind, Limits* limits, Shareable allowShared)
 {
     JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
     if (!initialAtom)
         return false;
     RootedId initialId(cx, AtomToId(initialAtom));
 
     RootedValue initialVal(cx);
     if (!GetProperty(cx, obj, obj, initialId, &initialVal))
@@ -336,36 +336,65 @@ GetLimits(JSContext* cx, HandleObject ob
     if (!EnforceRangeU32(cx, initialVal, maxInitial, kind, "initial size", &limits->initial))
         return false;
 
     JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
     if (!maximumAtom)
         return false;
     RootedId maximumId(cx, AtomToId(maximumAtom));
 
-    bool found;
-    if (!HasProperty(cx, obj, maximumId, &found))
+    bool foundMaximum;
+    if (!HasProperty(cx, obj, maximumId, &foundMaximum))
         return false;
 
-    if (found) {
+    if (foundMaximum) {
         RootedValue maxVal(cx);
         if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
             return false;
 
         limits->maximum.emplace();
         if (!EnforceRangeU32(cx, maxVal, maxMaximum, kind, "maximum size", limits->maximum.ptr()))
             return false;
 
         if (limits->initial > *limits->maximum) {
             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32,
                                      kind, "maximum size");
             return false;
         }
     }
 
+    limits->shared = Shareable::False;
+
+#ifdef ENABLE_WASM_THREAD_OPS
+    if (allowShared == Shareable::True) {
+        JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
+        if (!sharedAtom)
+            return false;
+        RootedId sharedId(cx, AtomToId(sharedAtom));
+
+        bool foundShared;
+        if (!HasProperty(cx, obj, sharedId, &foundShared))
+            return false;
+
+        if (foundShared) {
+            RootedValue sharedVal(cx);
+            if (!GetProperty(cx, obj, obj, sharedId, &sharedVal))
+                return false;
+
+            limits->shared = ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
+
+            if (limits->shared == Shareable::True && !foundMaximum) {
+                JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_MISSING_MAXIMUM,
+                                          kind);
+                return false;
+            }
+        }
+    }
+#endif
+
     return true;
 }
 
 // ============================================================================
 // WebAssembly.Module class and methods
 
 const ClassOps WasmModuleObject::classOps_ =
 {
@@ -1245,18 +1274,21 @@ WasmMemoryObject::construct(JSContext* c
 
     if (!args.get(0).isObject()) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "memory");
         return false;
     }
 
     RootedObject obj(cx, &args[0].toObject());
     Limits limits;
-    if (!GetLimits(cx, obj, MaxMemoryInitialPages, MaxMemoryMaximumPages, "Memory", &limits))
+    if (!GetLimits(cx, obj, MaxMemoryInitialPages, MaxMemoryMaximumPages, "Memory", &limits,
+                   Shareable::True))
+    {
         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)
@@ -1555,18 +1587,21 @@ WasmTableObject::construct(JSContext* cx
         return false;
 
     if (!StringEqualsAscii(elementStr, "anyfunc")) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
         return false;
     }
 
     Limits limits;
-    if (!GetLimits(cx, obj, MaxTableInitialLength, UINT32_MAX, "Table", &limits))
+    if (!GetLimits(cx, obj, MaxTableInitialLength, UINT32_MAX, "Table", &limits,
+                   Shareable::False))
+    {
         return false;
+    }
 
     RootedWasmTableObject table(cx, WasmTableObject::create(cx, limits));
     if (!table)
         return false;
 
     args.rval().setObject(*table);
     return true;
 }
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -108,16 +108,17 @@ class WasmToken
         Nop,
         Offset,
         OpenParen,
         Param,
         Result,
         Return,
         SetGlobal,
         SetLocal,
+        Shared,
         SignedInteger,
         Start,
         Store,
         Table,
         TeeLocal,
         TernaryOpcode,
         Text,
         Then,
@@ -309,16 +310,17 @@ class WasmToken
           case NegativeZero:
           case Local:
           case Module:
           case Name:
           case Offset:
           case OpenParen:
           case Param:
           case Result:
+          case Shared:
           case SignedInteger:
           case Start:
           case Table:
           case Text:
           case Then:
           case Type:
           case UnsignedInteger:
           case ValueType:
@@ -1440,16 +1442,20 @@ WasmTokenStream::next()
 
       case 's':
         if (consume(u"select"))
             return WasmToken(WasmToken::TernaryOpcode, Op::Select, begin, cur_);
         if (consume(u"set_global"))
             return WasmToken(WasmToken::SetGlobal, begin, cur_);
         if (consume(u"set_local"))
             return WasmToken(WasmToken::SetLocal, begin, cur_);
+#ifdef ENABLE_WASM_THREAD_OPS
+        if (consume(u"shared"))
+            return WasmToken(WasmToken::Shared, begin, cur_);
+#endif
         if (consume(u"start"))
             return WasmToken(WasmToken::Start, begin, cur_);
         break;
 
       case 't':
         if (consume(u"table"))
             return WasmToken(WasmToken::Table, begin, cur_);
         if (consume(u"tee_local"))
@@ -2788,28 +2794,39 @@ ParseDataSegment(WasmParseContext& c)
         if (!fragments.append(text.text()))
             return nullptr;
     }
 
     return new(c.lifo) AstDataSegment(offset, Move(fragments));
 }
 
 static bool
-ParseLimits(WasmParseContext& c, Limits* limits)
+ParseLimits(WasmParseContext& c, Limits* limits, Shareable allowShared)
 {
     WasmToken initial;
     if (!c.ts.match(WasmToken::Index, &initial, c.error))
         return false;
 
     Maybe<uint32_t> maximum;
     WasmToken token;
     if (c.ts.getIf(WasmToken::Index, &token))
         maximum.emplace(token.index());
 
-    *limits = Limits(initial.index(), maximum);
+    Shareable shared = Shareable::False;
+    if (c.ts.getIf(WasmToken::Shared, &token)) {
+        // A missing maximum is caught later.
+        if (allowShared == Shareable::True)
+            shared = Shareable::True;
+        else {
+            c.ts.generateError(token, "'shared' not allowed", c.error);
+            return false;
+        }
+    }
+
+    *limits = Limits(initial.index(), maximum, shared);
     return true;
 }
 
 static bool
 ParseMemory(WasmParseContext& c, WasmToken token, AstModule* module)
 {
     AstName name = c.ts.getIfName();
 
@@ -2818,17 +2835,17 @@ ParseMemory(WasmParseContext& c, WasmTok
         if (c.ts.getIf(WasmToken::Import)) {
             InlineImport names;
             if (!ParseInlineImport(c, &names))
                 return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return false;
 
             Limits memory;
-            if (!ParseLimits(c, &memory))
+            if (!ParseLimits(c, &memory, Shareable::True))
                 return false;
 
             auto* imp = new(c.lifo) AstImport(name, names.module.text(), names.field.text(),
                                               DefinitionKind::Memory, memory);
             return imp && module->append(imp);
         }
 
         if (c.ts.getIf(WasmToken::Export)) {
@@ -2866,27 +2883,27 @@ ParseMemory(WasmParseContext& c, WasmTok
             if (!segment || !module->append(segment))
                 return false;
 
             pages = AlignBytes<size_t>(totalLength, PageSize) / PageSize;
             if (pages != uint32_t(pages))
                 return false;
         }
 
-        if (!module->addMemory(name, Limits(pages, Some(pages))))
+        if (!module->addMemory(name, Limits(pages, Some(pages), Shareable::False)))
             return false;
 
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
 
         return true;
     }
 
     Limits memory;
-    if (!ParseLimits(c, &memory))
+    if (!ParseLimits(c, &memory, Shareable::True))
         return false;
 
     return module->addMemory(name, memory);
 }
 
 static bool
 ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module)
 {
@@ -2926,17 +2943,17 @@ ParseElemType(WasmParseContext& c)
 {
     // Only AnyFunc is allowed at the moment.
     return c.ts.match(WasmToken::AnyFunc, c.error);
 }
 
 static bool
 ParseTableSig(WasmParseContext& c, Limits* table)
 {
-    return ParseLimits(c, table) &&
+    return ParseLimits(c, table, Shareable::False) &&
            ParseElemType(c);
 }
 
 static AstImport*
 ParseImport(WasmParseContext& c, AstModule* module)
 {
     AstName name = c.ts.getIfName();
 
@@ -2951,17 +2968,17 @@ ParseImport(WasmParseContext& c, AstModu
     AstRef sigRef;
     WasmToken openParen;
     if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
         if (c.ts.getIf(WasmToken::Memory)) {
             if (name.empty())
                 name = c.ts.getIfName();
 
             Limits memory;
-            if (!ParseLimits(c, &memory))
+            if (!ParseLimits(c, &memory, Shareable::True))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
             return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
                                          DefinitionKind::Memory, memory);
         }
         if (c.ts.getIf(WasmToken::Table)) {
             if (name.empty())
@@ -3158,17 +3175,17 @@ ParseTable(WasmParseContext& c, WasmToke
 
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return false;
 
     uint32_t numElements = uint32_t(elems.length());
     if (numElements != elems.length())
         return false;
 
-    if (!module->addTable(name, Limits(numElements, Some(numElements))))
+    if (!module->addTable(name, Limits(numElements, Some(numElements), Shareable::False)))
         return false;
 
     auto* zero = new(c.lifo) AstConst(Val(uint32_t(0)));
     if (!zero)
         return false;
 
     AstElemSegment* segment = new(c.lifo) AstElemSegment(zero, Move(elems));
     return segment && module->append(segment);
@@ -4398,17 +4415,22 @@ EncodeBytes(Encoder& e, AstName wasmName
     TwoByteChars range(wasmName.begin(), wasmName.length());
     UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str());
     return utf8 && e.writeBytes(utf8.get(), strlen(utf8.get()));
 }
 
 static bool
 EncodeLimits(Encoder& e, const Limits& limits)
 {
-    uint32_t flags = limits.maximum ? 1 : 0;
+    uint32_t flags = limits.maximum
+                   ? uint32_t(MemoryTableFlags::HasMaximum)
+                   : uint32_t(MemoryTableFlags::Default);
+    if (limits.shared == Shareable::True)
+        flags |= uint32_t(MemoryTableFlags::IsShared);
+
     if (!e.writeVarU32(flags))
         return false;
 
     if (!e.writeVarU32(limits.initial))
         return false;
 
     if (limits.maximum) {
         if (!e.writeVarU32(*limits.maximum))
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1392,26 +1392,37 @@ struct Assumptions
 // A Module can either be asm.js or wasm.
 
 enum ModuleKind
 {
     Wasm,
     AsmJS
 };
 
+enum class Shareable
+{
+    False,
+    True
+};
+
 // Represents the resizable limits of memories and tables.
 
 struct Limits
 {
     uint32_t initial;
     Maybe<uint32_t> maximum;
 
+    // `shared` is Shareable::False for tables but may be Shareable::True for
+    // memories.
+    Shareable shared;
+
     Limits() = default;
-    explicit Limits(uint32_t initial, const Maybe<uint32_t>& maximum = Nothing())
-      : initial(initial), maximum(maximum)
+    explicit Limits(uint32_t initial, const Maybe<uint32_t>& maximum = Nothing(),
+                    Shareable shared = Shareable::False)
+      : initial(initial), maximum(maximum), shared(shared)
     {}
 };
 
 // TableDesc describes a table as well as the offset of the table's base pointer
 // in global memory. Currently, wasm only has "any function" and asm.js only
 // "typed function".
 
 enum class TableKind
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -864,42 +864,59 @@ DecodeSignatureIndex(Decoder& d, const S
 
     if (*sigIndex >= sigs.length())
         return d.fail("signature index out of range");
 
     return true;
 }
 
 static bool
-DecodeLimits(Decoder& d, Limits* limits)
+DecodeLimits(Decoder& d, Limits* limits, Shareable allowShared = Shareable::False)
 {
     uint8_t flags;
     if (!d.readFixedU8(&flags))
         return d.fail("expected flags");
 
-    if (flags & ~uint8_t(0x1))
-        return d.failf("unexpected bits set in flags: %" PRIu32, (flags & ~uint8_t(0x1)));
+    uint8_t mask = allowShared == Shareable::True
+                   ? uint8_t(MemoryMasks::AllowShared)
+                   : uint8_t(MemoryMasks::AllowUnshared);
+
+    if (flags & ~uint8_t(mask))
+        return d.failf("unexpected bits set in flags: %" PRIu32, (flags & ~uint8_t(mask)));
 
     if (!d.readVarU32(&limits->initial))
         return d.fail("expected initial length");
 
-    if (flags & 0x1) {
+    if (flags & uint8_t(MemoryTableFlags::HasMaximum)) {
         uint32_t maximum;
         if (!d.readVarU32(&maximum))
             return d.fail("expected maximum length");
 
         if (limits->initial > maximum) {
             return d.failf("memory size minimum must not be greater than maximum; "
                            "maximum length %" PRIu32 " is less than initial length %" PRIu32,
                            maximum, limits->initial);
         }
 
         limits->maximum.emplace(maximum);
     }
 
+    limits->shared = Shareable::False;
+
+#ifdef ENABLE_WASM_THREAD_OPS
+    if (allowShared == Shareable::True) {
+        if ((flags & uint8_t(MemoryTableFlags::IsShared)) && !(flags & uint8_t(MemoryTableFlags::HasMaximum)))
+            return d.fail("maximum length required for shared memory");
+
+        limits->shared = (flags & uint8_t(MemoryTableFlags::IsShared))
+                       ? Shareable::True
+                       : Shareable::False;
+    }
+#endif
+
     return true;
 }
 
 static bool
 DecodeTableLimits(Decoder& d, TableDescVector* tables)
 {
     uint8_t elementType;
     if (!d.readFixedU8(&elementType))
@@ -959,17 +976,17 @@ DecodeGlobalType(Decoder& d, ValType* ty
 
 static bool
 DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env)
 {
     if (env->usesMemory())
         return d.fail("already have default memory");
 
     Limits memory;
-    if (!DecodeLimits(d, &memory))
+    if (!DecodeLimits(d, &memory, Shareable::True))
         return false;
 
     if (memory.initial > MaxMemoryInitialPages)
         return d.fail("initial memory size too big");
 
     CheckedInt<uint32_t> initialBytes = memory.initial;
     initialBytes *= PageSize;
     MOZ_ASSERT(initialBytes.isValid());
@@ -982,17 +999,19 @@ DecodeMemoryLimits(Decoder& d, ModuleEnv
         CheckedInt<uint32_t> maximumBytes = *memory.maximum;
         maximumBytes *= PageSize;
 
         // Clamp the maximum memory value to UINT32_MAX; it's not semantically
         // visible since growing will fail for values greater than INT32_MAX.
         memory.maximum = Some(maximumBytes.isValid() ? maximumBytes.value() : UINT32_MAX);
     }
 
-    env->memoryUsage = MemoryUsage::Unshared;
+    env->memoryUsage = memory.shared == Shareable::True
+                     ? MemoryUsage::Shared
+                     : MemoryUsage::Unshared;
     env->minMemoryLength = memory.initial;
     env->maxMemoryLength = memory.maximum;
     return true;
 }
 
 static bool
 DecodeImport(Decoder& d, ModuleEnvironment* env)
 {