Bug 1299359 - Odin: put asm.js Atomics/SAB support behind wasmTestMode (r=lth)
authorLuke Wagner <luke@mozilla.com>
Mon, 05 Sep 2016 09:50:43 -0500
changeset 354012 0599e881d7a4c5f3846df3e8b8925302c478c264
parent 353993 797a17d8a60417b2902e063ca24c6e395733cebb
child 354013 9c52e335a78355041ad09f68732cd58704ff825d
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1299359
milestone51.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 1299359 - Odin: put asm.js Atomics/SAB support behind wasmTestMode (r=lth) MozReview-Commit-ID: 6nKQ3giYnic
js/src/asmjs/AsmJS.cpp
js/src/jit-test/tests/asm.js/gating.js
js/src/jit-test/tests/asm.js/sta-transition.js
js/src/jit-test/tests/asm.js/testAtomic-effect.js
js/src/jit-test/tests/asm.js/testAtomics.js
js/src/jit-test/tests/asm.js/testBug1155176.js
js/src/jit-test/tests/asm.js/testBug1164391.js
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -1962,16 +1962,19 @@ class MOZ_STACK_CLASS ModuleValidator
         AsmJSGlobal g(AsmJSGlobal::Constant, Move(fieldChars));
         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.wasmTestMode)
+            return failCurrentOffset("asm.js Atomics only enabled in wasm test mode");
+
         atomicsPresent_ = true;
 
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
         Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
         if (!global)
@@ -7803,16 +7806,19 @@ CheckBuffer(JSContext* cx, const AsmJSMe
             return false;
         return LinkFail(cx, msg.get());
     }
 
     if (buffer->is<ArrayBufferObject>()) {
         Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
         if (!ArrayBufferObject::prepareForAsmJS(cx, abheap))
             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");
     }
 
     return true;
 }
 
 static bool
 GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
            HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
--- a/js/src/jit-test/tests/asm.js/gating.js
+++ b/js/src/jit-test/tests/asm.js/gating.js
@@ -13,57 +13,59 @@
 // values that are not the expected built-in values and the link will
 // fail as desired.
 
 load(libdir + "asm.js");
 
 if (!isAsmJSCompilationAvailable())
     quit(0);
 
+setJitCompilerOption('wasm.test-mode', 1);
+
 if (!this.Atomics) {
     this.Atomics = { load: function (x, y) { return 0 },
 		     store: function (x, y, z) { return 0 },
 		     exchange: function (x, y, z) { return 0 },
 		     add: function (x, y, z) { return 0 },
 		     sub: function (x, y, z) { return 0 },
 		     and: function (x, y, z) { return 0 },
 		     or: function (x, y, z) { return 0 },
 		     xor: function (x, y, z) { return 0 },
 		     compareExchange: function (x, y, z, w) { return 0 }
 		   };
 }
 
 
-function module_a(stdlib, foreign, heap) {
+var module_a = asmCompile("stdlib", "foreign", "heap", `
     "use asm";
 
     var ld = stdlib.Atomics.load;
 
     function f() { return 0; }
     return { f:f };
-}
+`);
 
-function module_b(stdlib, foreign, heap) {
+var module_b = asmCompile("stdlib", "foreign", "heap", `
     "use asm";
 
     var ld = stdlib.Atomics.load;
     var i32a = new stdlib.Int32Array(heap);
 
     function f() { return 0; }
     return { f:f };
-}
+`);
 
-function module_c(stdlib, foreign, heap) {
+var module_c = asmCompile("stdlib", "foreign", "heap", `
     "use asm";
 
     var i32a = new stdlib.Int32Array(heap);
 
     function f() { return 0; }
     return { f:f };
-}
+`);
 
 assertEq(isAsmJSModule(module_a), true);
 assertEq(isAsmJSModule(module_b), true);
 assertEq(isAsmJSModule(module_c), true);
 
 if (!this.SharedArrayBuffer) {
     assertAsmLinkFail(module_a, this, {}, new ArrayBuffer(65536));  // Buffer is ignored, Atomics are bad
 } else {
--- a/js/src/jit-test/tests/asm.js/sta-transition.js
+++ b/js/src/jit-test/tests/asm.js/sta-transition.js
@@ -1,56 +1,60 @@
 // Test the inference of shared memory in asm.js.
 //
 // These should not be run with --no-asmjs, the guard below checks this.
 
+load(libdir + "asm.js");
+
 if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
     quit(0);
 
+setJitCompilerOption('wasm.test-mode', 1);
+
 //////////////////////////////////////////////////////////////////////
 //
 // Int8Array can be used on SharedArrayBuffer, if atomics are present
 
-function m1(stdlib, ffi, heap) {
+var m1 = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
 
     var i8 = new stdlib.Int8Array(heap);
     var add = stdlib.Atomics.add;
 
     function f() {
 	add(i8, 0, 1);
 	return 37;
     }
 
     return { f:f }
-}
+`);
 
 assertEq(isAsmJSModule(m1), true);
 
 var { f } = m1(this, {}, new SharedArrayBuffer(65536));
 assertEq(f(), 37);
 
 //////////////////////////////////////////////////////////////////////
 //
 // Int8Array cannot be used on SharedArrayBuffer if atomics are not imported.
 // One argument for the restriction is that there are some optimizations
 // that are legal if the memory is known not to be shared that are illegal
 // when it is shared.
 
-function m4(stdlib, ffi, heap) {
+var m4 = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
 
     var i8 = new stdlib.Int8Array(heap);
 
     function i() {
 	return i8[0]|0;
     }
 
     return { i:i }
-}
+`);
 
 assertEq(isAsmJSModule(m4), true);
 
 // An error is not actually thrown because the link failure drops us
 // back to JS execution and then the Int8Array constructor will copy data
 // from the SharedArrayBuffer.
 //
 // Running the shell with -w you should see an error here.
--- a/js/src/jit-test/tests/asm.js/testAtomic-effect.js
+++ b/js/src/jit-test/tests/asm.js/testAtomic-effect.js
@@ -1,13 +1,14 @@
 // |jit-test| test-also-noasmjs
 if (!this.Atomics)
     quit();
 
 load(libdir + "asm.js");
+setJitCompilerOption('wasm.test-mode', 1);
 
 var code = `
     "use asm";
 
     var HEAP32 = new stdlib.Int32Array(heap);
     var add = stdlib.Atomics.add;
     var load = stdlib.Atomics.load;
     var _emscripten_asm_const_int=ffi._emscripten_asm_const_int;
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -4,16 +4,18 @@ if (!this.SharedArrayBuffer || !this.Ato
     quit();
 
 // The code duplication below is very far from elegant but provides
 // flexibility that comes in handy several places.
 
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
+setJitCompilerOption('wasm.test-mode', 1);
+
 var loadModule_int32_code =
     USE_ASM + `
     var atomic_load = stdlib.Atomics.load;
     var atomic_store = stdlib.Atomics.store;
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
--- a/js/src/jit-test/tests/asm.js/testBug1155176.js
+++ b/js/src/jit-test/tests/asm.js/testBug1155176.js
@@ -1,28 +1,31 @@
 if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
     quit(0);
 
+load(libdir + "asm.js");
+setJitCompilerOption('wasm.test-mode', 1);
+
 // The way this is constructed, either the first module does not
 // verify as asm.js (if the >>>0 is left off, which was legal prior to
 // bug 1155176), or the results of the two modules have to be equal.
 
-function m(stdlib, ffi, heap) {
+var m = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
 
     var view = new stdlib.Uint32Array(heap);
     var cas = stdlib.Atomics.compareExchange;
     var hi = ffi.hi;
 
     function run() {
 	hi(+(cas(view, 37, 0, 0)>>>0));
     }
 
     return run;
-}
+`);
 
 assertEq(isAsmJSModule(m), true);
 
 function nonm(stdlib, ffi, heap) {
 
     var view = new stdlib.Uint32Array(heap);
     var cas = stdlib.Atomics.compareExchange;
     var hi = ffi.hi;
--- a/js/src/jit-test/tests/asm.js/testBug1164391.js
+++ b/js/src/jit-test/tests/asm.js/testBug1164391.js
@@ -1,25 +1,27 @@
 if (!this.SharedArrayBuffer)
     quit(0);
 
+load(libdir + "asm.js");
 load(libdir + "asserts.js");
+setJitCompilerOption('wasm.test-mode', 1);
 
-function m(stdlib, ffi, heap) {
+var m = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
     var HEAP32 = new stdlib.Int32Array(heap);
     var add = stdlib.Atomics.add;
     var load = stdlib.Atomics.load;
     function add_sharedEv(i1) {
         i1 = i1 | 0;
         load(HEAP32, i1 >> 2);
         add(HEAP32, i1 >> 2, 1);
         load(HEAP32, i1 >> 2);
     }
     return {add_sharedEv:add_sharedEv};
-}
+`);
 
 if (isAsmJSCompilationAvailable())
     assertEq(isAsmJSModule(m), true);
 
 var sab = new SharedArrayBuffer(65536);
 var {add_sharedEv} = m(this, {}, sab);
 assertErrorMessage(() => add_sharedEv(sab.byteLength), RangeError, /out-of-range index/);
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -76,16 +76,17 @@ MarkValidRegion(void* addr, size_t len)
 #if defined(WASM_HUGE_MEMORY)
 // 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()
 {
+    MOZ_RELEASE_ASSERT(jit::JitOptions.wasmTestMode);
     MOZ_RELEASE_ASSERT(sizeof(SharedArrayRawBuffer) < gc::SystemPageSize());
     return wasm::MappedSize + gc::SystemPageSize();
 }
 
 // If there are too many 4GB buffers live we run up against system resource
 // exhaustion (address space or number of memory map descriptors), see
 // bug 1068684, bug 1073934 for details.  The limiting case seems to be
 // Windows Vista Home 64-bit, where the per-process address space is limited
@@ -108,23 +109,22 @@ SharedArrayRawBuffer::New(JSContext* cx,
     // 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);
 
     // Add a page for the header and round to a page boundary.
     uint32_t allocSize = SharedArrayAllocSize(length);
     if (allocSize <= length)
         return nullptr;
+
+    bool preparedForAsmJS = jit::JitOptions.wasmTestMode && IsValidAsmJSHeapLength(length);
+
     void* p = nullptr;
-    if (!IsValidAsmJSHeapLength(length)) {
-        p = MapMemory(allocSize, true);
-        if (!p)
-            return nullptr;
-    } else {
 #ifdef WASM_HUGE_MEMORY
+    if (preparedForAsmJS) {
         // Test >= to guard against the case where multiple extant runtimes
         // race to allocate.
         if (++numLive >= maxLive) {
             JSRuntime* rt = cx->runtime();
             if (rt->largeAllocationFailureCallback)
                 rt->largeAllocationFailureCallback(rt->largeAllocationFailureCallbackData);
             if (numLive >= maxLive) {
                 numLive--;
@@ -138,72 +138,73 @@ SharedArrayRawBuffer::New(JSContext* cx,
             return nullptr;
         }
 
         if (!MarkValidRegion(p, allocSize)) {
             UnmapMemory(p, SharedArrayMappedSize());
             numLive--;
             return nullptr;
         }
-#   if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
+
+# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
         // Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
         VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)p + allocSize,
                                                        SharedArrayMappedSize() - allocSize);
-#   endif
-#else
+# endif
+    } else
+#endif
+    {
         p = MapMemory(allocSize, true);
         if (!p)
             return nullptr;
-#endif
     }
+
     uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
     uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
-    SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length);
+    SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length, preparedForAsmJS);
     MOZ_ASSERT(rawbuf->length == length); // Deallocation needs this
     return rawbuf;
 }
 
 void
 SharedArrayRawBuffer::addReference()
 {
     MOZ_ASSERT(this->refcount_ > 0);
     ++this->refcount_; // Atomic.
 }
 
 void
 SharedArrayRawBuffer::dropReference()
 {
     // Drop the reference to the buffer.
     uint32_t refcount = --this->refcount_; // Atomic.
+    if (refcount)
+        return;
 
     // If this was the final reference, release the buffer.
-    if (refcount == 0) {
-        SharedMem<uint8_t*> p = this->dataPointerShared() - gc::SystemPageSize();
 
-        MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
+    SharedMem<uint8_t*> p = this->dataPointerShared() - gc::SystemPageSize();
+    MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
 
-        uint8_t* address = p.unwrap(/*safe - only reference*/);
-        uint32_t allocSize = SharedArrayAllocSize(this->length);
-        if (!IsValidAsmJSHeapLength(this->length)) {
-            UnmapMemory(address, allocSize);
-        } else {
+    uint8_t* address = p.unwrap(/*safe - only reference*/);
+    uint32_t allocSize = SharedArrayAllocSize(this->length);
+
 #if defined(WASM_HUGE_MEMORY)
-            numLive--;
-            UnmapMemory(address, SharedArrayMappedSize());
-#       if defined(MOZ_VALGRIND) \
-           && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
-            // Tell Valgrind/Memcheck to recommence reporting accesses in the
-            // previously-inaccessible region.
-            VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(address,
-                                                          SharedArrayMappedSize());
-#       endif
-#else
-            UnmapMemory(address, allocSize);
+    if (this->preparedForAsmJS) {
+        numLive--;
+        UnmapMemory(address, SharedArrayMappedSize());
+# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
+        // Tell Valgrind/Memcheck to recommence reporting accesses in the
+        // previously-inaccessible region.
+        VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(address, SharedArrayMappedSize());
+# endif
+    } else
 #endif
-        }
+    {
+        UnmapMemory(address, allocSize);
     }
 }
 
 const JSFunctionSpec SharedArrayBufferObject::jsfuncs[] = {
     /* Nothing yet */
     JS_FS_END
 };
 
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -41,25 +41,27 @@ class FutexWaiter;
  * the data array using a constant pointer, instead of computing its
  * address.
  */
 class SharedArrayRawBuffer
 {
   private:
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
     uint32_t length;
+    bool preparedForAsmJS;
 
     // A list of structures representing tasks waiting on some
     // location within this buffer.
     FutexWaiter* waiters_;
 
   protected:
-    SharedArrayRawBuffer(uint8_t* buffer, uint32_t length)
+    SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, bool preparedForAsmJS)
       : refcount_(1),
         length(length),
+        preparedForAsmJS(preparedForAsmJS),
         waiters_(nullptr)
     {
         MOZ_ASSERT(buffer == dataPointerShared());
     }
 
   public:
     static SharedArrayRawBuffer* New(JSContext* cx, uint32_t length);
 
@@ -79,16 +81,20 @@ class SharedArrayRawBuffer
         uint8_t* ptr = reinterpret_cast<uint8_t*>(const_cast<SharedArrayRawBuffer*>(this));
         return SharedMem<uint8_t*>::shared(ptr + sizeof(SharedArrayRawBuffer));
     }
 
     uint32_t byteLength() const {
         return length;
     }
 
+    bool isPreparedForAsmJS() const {
+        return preparedForAsmJS;
+    }
+
     uint32_t refcount() const { return refcount_; }
 
     void addReference();
     void dropReference();
 };
 
 /*
  * SharedArrayBufferObject
@@ -152,16 +158,19 @@ class SharedArrayBufferObject : public A
         // 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();
     }
+    bool isPreparedForAsmJS() const {
+        return rawBufferObject()->isPreparedForAsmJS();
+    }
 
     SharedMem<uint8_t*> dataPointerShared() const {
         return rawBufferObject()->dataPointerShared();
     }
 
 private:
     void acceptRawBuffer(SharedArrayRawBuffer* buffer);
     void dropRawBuffer();