Bug 1619196 - allow atomics on non-shared memory part 1: validation and compilation. r=rhunt
authorLars T Hansen <lhansen@mozilla.com>
Tue, 30 Jun 2020 11:03:43 +0000
changeset 537968 d6844af829249448ca55df4d217fd800dc9d3633
parent 537967 eb95d039d9a025b22a54432bb26904eb5ab61494
child 537969 191dce06f4b461e85bc30c749bf5c85144fb46c8
push id37555
push usercbrindusan@mozilla.com
push dateTue, 30 Jun 2020 14:45:59 +0000
treeherdermozilla-central@933c9f34edfa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1619196
milestone80.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1619196 - allow atomics on non-shared memory part 1: validation and compilation. r=rhunt Pretty simple change: remove the validation guards on plain atomic operations, and distinguish between shared and unshared memory for wait and notify. Per spec, wait checks for sharedness before checking for alignment or bounds, and notify returns 0 after checking for alignment and bounds. Differential Revision: https://phabricator.services.mozilla.com/D81319
js/src/js.msg
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmOpIter.h
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -431,16 +431,17 @@ MSG_DEF(JSMSG_WASM_BAD_VAL_TYPE,       0
 MSG_DEF(JSMSG_WASM_BAD_GLOBAL_TYPE,    0, JSEXN_TYPEERR,     "bad type for a WebAssembly.Global")
 MSG_DEF(JSMSG_WASM_NO_TRANSFER,        0, JSEXN_TYPEERR,     "cannot transfer WebAssembly/asm.js ArrayBuffer")
 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")
 MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE,   0, JSEXN_TYPEERR,     "can't set value of immutable global")
 MSG_DEF(JSMSG_WASM_TYPEREF_FROM_JS,    0, JSEXN_TYPEERR,     "conversion from JavaScript value to WebAssembly typed ref unimplemented")
 MSG_DEF(JSMSG_WASM_TYPEREF_TO_JS,      0, JSEXN_TYPEERR,     "conversion from WebAssembly typed ref to JavaScript value unimplemented")
 MSG_DEF(JSMSG_WASM_WRONG_NUMBER_OF_VALUES, 2, JSEXN_TYPEERR, "wrong number of values returned by JavaScript to WebAssembly (expected {0}, got {1})")
+MSG_DEF(JSMSG_WASM_NONSHARED_WAIT ,    0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory")
 
 // 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/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -691,16 +691,22 @@ Instance::callImport_funcref(Instance* i
   return byteLength / wasm::PageSize;
 }
 
 template <typename T>
 static int32_t PerformWait(Instance* instance, uint32_t byteOffset, T value,
                            int64_t timeout_ns) {
   JSContext* cx = TlsContext.get();
 
+  if (!instance->memory()->isShared()) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_WASM_NONSHARED_WAIT);
+    return -1;
+  }
+
   if (byteOffset & (sizeof(T) - 1)) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_UNALIGNED_ACCESS);
     return -1;
   }
 
   if (byteOffset + sizeof(T) > instance->memory()->volatileMemoryLength()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
@@ -758,16 +764,20 @@ static int32_t PerformWait(Instance* ins
   }
 
   if (byteOffset >= instance->memory()->volatileMemoryLength()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_OUT_OF_BOUNDS);
     return -1;
   }
 
+  if (!instance->memory()->isShared()) {
+    return 0;
+  }
+
   int64_t woken = atomics_notify_impl(instance->sharedMemoryBuffer(),
                                       byteOffset, int64_t(count));
 
   if (woken > INT32_MAX) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_WAKE_OVERFLOW);
     return -1;
   }
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -1851,21 +1851,16 @@ inline bool OpIter<Policy>::readOldCallI
   return push(ResultType::Vector(funcType.results()));
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readWake(LinearMemoryAddress<Value>* addr,
                                      Value* count) {
   MOZ_ASSERT(Classify(op_) == OpKind::Wake);
 
-  if (!env_.usesSharedMemory()) {
-    return fail(
-        "can't touch memory with atomic operations without shared memory");
-  }
-
   if (!popWithType(ValType::I32, count)) {
     return false;
   }
 
   uint32_t byteSize = 4;  // Per spec; smallest WAIT is i32.
 
   if (!readLinearMemoryAddressAligned(byteSize, addr)) {
     return false;
@@ -1876,21 +1871,16 @@ inline bool OpIter<Policy>::readWake(Lin
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readWait(LinearMemoryAddress<Value>* addr,
                                      ValType valueType, uint32_t byteSize,
                                      Value* value, Value* timeout) {
   MOZ_ASSERT(Classify(op_) == OpKind::Wait);
 
-  if (!env_.usesSharedMemory()) {
-    return fail(
-        "can't touch memory with atomic operations without shared memory");
-  }
-
   if (!popWithType(ValType::I64, timeout)) {
     return false;
   }
 
   if (!popWithType(valueType, value)) {
     return false;
   }
 
@@ -1916,40 +1906,30 @@ inline bool OpIter<Policy>::readFence() 
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readAtomicLoad(LinearMemoryAddress<Value>* addr,
                                            ValType resultType,
                                            uint32_t byteSize) {
   MOZ_ASSERT(Classify(op_) == OpKind::AtomicLoad);
 
-  if (!env_.usesSharedMemory()) {
-    return fail(
-        "can't touch memory with atomic operations without shared memory");
-  }
-
   if (!readLinearMemoryAddressAligned(byteSize, addr)) {
     return false;
   }
 
   infalliblePush(resultType);
   return true;
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readAtomicStore(LinearMemoryAddress<Value>* addr,
                                             ValType resultType,
                                             uint32_t byteSize, Value* value) {
   MOZ_ASSERT(Classify(op_) == OpKind::AtomicStore);
 
-  if (!env_.usesSharedMemory()) {
-    return fail(
-        "can't touch memory with atomic operations without shared memory");
-  }
-
   if (!popWithType(resultType, value)) {
     return false;
   }
 
   if (!readLinearMemoryAddressAligned(byteSize, addr)) {
     return false;
   }
 
@@ -1957,21 +1937,16 @@ inline bool OpIter<Policy>::readAtomicSt
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readAtomicRMW(LinearMemoryAddress<Value>* addr,
                                           ValType resultType, uint32_t byteSize,
                                           Value* value) {
   MOZ_ASSERT(Classify(op_) == OpKind::AtomicBinOp);
 
-  if (!env_.usesSharedMemory()) {
-    return fail(
-        "can't touch memory with atomic operations without shared memory");
-  }
-
   if (!popWithType(resultType, value)) {
     return false;
   }
 
   if (!readLinearMemoryAddressAligned(byteSize, addr)) {
     return false;
   }
 
@@ -1982,21 +1957,16 @@ inline bool OpIter<Policy>::readAtomicRM
 template <typename Policy>
 inline bool OpIter<Policy>::readAtomicCmpXchg(LinearMemoryAddress<Value>* addr,
                                               ValType resultType,
                                               uint32_t byteSize,
                                               Value* oldValue,
                                               Value* newValue) {
   MOZ_ASSERT(Classify(op_) == OpKind::AtomicCompareExchange);
 
-  if (!env_.usesSharedMemory()) {
-    return fail(
-        "can't touch memory with atomic operations without shared memory");
-  }
-
   if (!popWithType(resultType, newValue)) {
     return false;
   }
 
   if (!popWithType(resultType, oldValue)) {
     return false;
   }