Bug 1205390 - make Odin accept TypedArray constructors for shared memory. r=luke
authorLars T Hansen <lhansen@mozilla.com>
Fri, 23 Oct 2015 13:19:08 +0200
changeset 304377 8f72e3889e802851d568f7a3cefa0a2979f9ba3d
parent 304376 af7f101376375a3d763bad927596b104cb638e95
child 304378 590ee345a6ed78972c597d38ab7d2b7863bf0123
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1205390
milestone44.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 1205390 - make Odin accept TypedArray constructors for shared memory. r=luke
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/jit-test/tests/asm.js/sta-transition.js
js/src/jit-test/tests/asm.js/testAtomics.js
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -225,17 +225,17 @@ ValidateArrayView(JSContext* cx, AsmJSMo
         return true;
 
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, field, &v))
         return false;
 
     bool tac = IsTypedArrayConstructor(v, global.viewType());
     bool stac = IsSharedTypedArrayConstructor(v, global.viewType());
-    if (!((tac || stac) && stac == isShared))
+    if (!(tac || (stac && isShared)))
         return LinkFail(cx, "bad typed array constructor");
 
     return true;
 }
 
 static bool
 ValidateByteLength(JSContext* cx, HandleValue globalVal)
 {
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -309,16 +309,20 @@ class AsmJSModule
         PropertyName* maybeViewName() const {
             MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == SharedArrayView || pod.which_ == ArrayViewCtor);
             return name_;
         }
         Scalar::Type viewType() const {
             MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == SharedArrayView || pod.which_ == ArrayViewCtor);
             return pod.u.viewType_;
         }
+        void makeViewShared() {
+            MOZ_ASSERT(pod.which_ == ArrayView);
+            pod.which_ = SharedArrayView;
+        }
         PropertyName* mathName() const {
             MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
             return name_;
         }
         PropertyName* atomicsName() const {
             MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
             return name_;
         }
@@ -1101,16 +1105,25 @@ class AsmJSModule
     Global& global(unsigned i) {
         return globals_[i];
     }
     bool isValidViewSharedness(bool shared) const {
         if (pod.hasArrayView_)
             return pod.isSharedView_ == shared;
         return !pod.isSharedView_ || shared;
     }
+    void setViewsAreShared() {
+        if (pod.hasArrayView_)
+            pod.isSharedView_ = true;
+        for (size_t i=0 ; i < globals_.length() ; i++) {
+            Global& g = globals_[i];
+            if (g.which() == Global::ArrayView)
+                g.makeViewShared();
+        }
+    }
 
     /*************************************************************************/
 
     void startFunctionBodies() {
         MOZ_ASSERT(!isFinishedWithModulePrologue());
         pod.funcPtrTableAndExitBytes_ = 0;
         MOZ_ASSERT(isFinishedWithModulePrologue());
     }
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -768,16 +768,20 @@ class MOZ_STACK_CLASS ModuleValidator
         Scalar::Type viewType() const {
             MOZ_ASSERT(isAnyArrayView());
             return u.viewInfo.viewType_;
         }
         bool viewIsSharedView() const {
             MOZ_ASSERT(isAnyArrayView());
             return u.viewInfo.isSharedView_;
         }
+        void setViewIsSharedView() {
+            MOZ_ASSERT(isAnyArrayView());
+            u.viewInfo.isSharedView_ = true;
+        }
         bool isMathFunction() const {
             return which_ == MathBuiltinFunction;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             MOZ_ASSERT(which_ == MathBuiltinFunction);
             return u.mathBuiltinFunc_;
         }
         bool isAtomicsFunction() const {
@@ -913,16 +917,17 @@ class MOZ_STACK_CLASS ModuleValidator
 
     UniquePtr<char[], JS::FreePolicy>       errorString_;
     uint32_t                                errorOffset_;
     bool                                    errorOverRecursed_;
 
     bool                                    canValidateChangeHeap_;
     bool                                    hasChangeHeap_;
     bool                                    supportsSimd_;
+    bool                                    atomicsPresent_;
 
     ScopedJSDeletePtr<ModuleCompileResults> compileResults_;
     DebugOnly<bool>                         finishedFunctionBodies_;
 
   public:
     ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
@@ -938,16 +943,17 @@ class MOZ_STACK_CLASS ModuleValidator
         moduleFunctionNode_(parser.pc->maybeFunction),
         moduleFunctionName_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
         canValidateChangeHeap_(false),
         hasChangeHeap_(false),
         supportsSimd_(cx->jitSupportsSimd()),
+        atomicsPresent_(false),
         compileResults_(nullptr),
         finishedFunctionBodies_(false)
     {
         MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleValidator() {
         if (errorString_) {
@@ -1150,16 +1156,17 @@ class MOZ_STACK_CLASS ModuleValidator
     bool addAtomicsBuiltinFunction(PropertyName* varName, AsmJSAtomicsBuiltinFunction func,
                                    PropertyName* fieldName)
     {
         if (!module_->addAtomicsBuiltinFunction(func, fieldName))
             return false;
         Global* global = moduleLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
         if (!global)
             return false;
+        atomicsPresent_ = true;
         global->u.atomicsBuiltinFunc_ = func;
         return globals_.putNew(varName, global);
     }
     bool addSimdCtor(PropertyName* varName, AsmJSSimdType type, PropertyName* fieldName) {
         if (!module_->addSimdCtor(type, fieldName))
             return false;
         Global* global = moduleLifo_.new_<Global>(Global::SimdCtor);
         if (!global)
@@ -1504,16 +1511,24 @@ class MOZ_STACK_CLASS ModuleValidator
             }
         }
 
         *module = module_.forget();
         return true;
     }
 
     void startFunctionBodies() {
+        if (atomicsPresent_) {
+            for (GlobalMap::Range r = globals_.all() ; !r.empty() ; r.popFront()) {
+                Global* g = r.front().value();
+                if (g->isAnyArrayView())
+                    g->setViewIsSharedView();
+            }
+            module_->setViewsAreShared();
+        }
         module_->startFunctionBodies();
     }
     bool finishFunctionBodies(ScopedJSDeletePtr<ModuleCompileResults>* compileResults) {
         // Take ownership of compilation results
         compileResults_ = compileResults->forget();
 
         // These must be done before the module is done with function bodies.
         for (size_t i = 0; i < compileResults_->numFunctionCounts(); ++i) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/sta-transition.js
@@ -0,0 +1,116 @@
+// Transitional test cases, useful while Odin accepts both
+// "Int32Array" and "SharedInt32Array" to construct a view onto shared
+// memory but the former only when an atomic operation is referenced,
+// as per spec.  Eventually it will stop accepting "SharedInt32Array",
+// because that name is going away.
+//
+// These should not run with --no-asmjs.
+
+//////////////////////////////////////////////////////////////////////
+//
+// Int8Array can be used on SharedArrayBuffer, if atomics are present
+
+function m1(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);
+
+//////////////////////////////////////////////////////////////////////
+//
+// SharedInt8Array can still be used on SharedArrayBuffer.
+// (SharedInt8Array will eventually disappear, and this
+// test case with it.)
+
+function m2(stdlib, ffi, heap) {
+    "use asm";
+
+    var i8 = new stdlib.SharedInt8Array(heap);
+    var add = stdlib.Atomics.add;
+
+    function g() {
+	add(i8, 0, 1);
+	return 42;
+    }
+
+    return { g:g }
+}
+
+assertEq(isAsmJSModule(m2), true);
+
+var { g } = m2(this, {}, new SharedArrayBuffer(65536));
+assertEq(g(), 42);
+
+//////////////////////////////////////////////////////////////////////
+//
+// SharedInt8Array still cannot be used on ArrayBuffer, even without
+// atomics present.
+// (SharedInt8Array will eventually disappear, and this
+// test case with it.)
+
+function m3(stdlib, ffi, heap) {
+    "use asm";
+
+    var i8 = new stdlib.SharedInt8Array(heap);
+
+    function h() {
+	return i8[0]|0;
+    }
+
+    return { h:h }
+}
+
+// Running the shell with -w you should see an error here.
+
+assertEq(isAsmJSModule(m3), true);
+try {
+    var wasThrown = false;
+    m3(this, {}, new ArrayBuffer(65536));
+}
+catch (e) {
+    wasThrown = true;
+}
+assertEq(wasThrown, true);
+
+//////////////////////////////////////////////////////////////////////
+//
+// 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) {
+    "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.
+
+var { i } = m4(this, {}, new SharedArrayBuffer(65536));
+assertEq(isAsmJSFunction(i), false);
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -1,32 +1,53 @@
 // |jit-test| test-also-noasmjs
-if (!this.SharedArrayBuffer || !this.SharedInt32Array || !this.Atomics)
+if (!this.SharedArrayBuffer || !this.Atomics)
     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");
 
+// This hack allows the test cases to run with --no-asmjs: the field values
+// are basically ignored in asm.js mode, and the correct (Firefox-specific)
+// field values are used in non-asm.js mode.  If run in a non-Firefox
+// browser that does not have the parallel type hierarchy this should also
+// work.
+//
+// This hack will be removed when the parallel type hierarchy is removed
+// from Firefox, bug 1176214.
+
+const atomicStdlib = {
+    Atomics:      Atomics,
+    Int8Array:    this.SharedInt8Array ? SharedInt8Array : Int8Array,
+    Uint8Array:   this.SharedUint8Array ? SharedUint8Array : Uint8Array,
+    Int16Array:   this.SharedInt16Array ? SharedInt16Array : Int16Array,
+    Uint16Array:  this.SharedUint16Array ? SharedUint16Array : Uint16Array,
+    Int32Array:   this.SharedInt32Array ? SharedInt32Array : Int32Array,
+    Uint32Array:  this.SharedUint32Array ? SharedUint32Array : Uint32Array,
+    Float32Array: this.SharedFloat32Array ? SharedFloat32Array : Float32Array,
+    Float64Array: this.SharedFloat64Array ? SharedFloat64Array : Float64Array
+};
+
 var loadModule_int32_code =
     USE_ASM + `
     var atomic_fence = stdlib.Atomics.fence;
     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;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
-    var i32a = new stdlib.SharedInt32Array(heap);
+    var i32a = new stdlib.Int32Array(heap);
 
     function do_fence() {
         atomic_fence();
     }
 
     // Load element 0
     function do_load() {
         var v = 0;
@@ -228,17 +249,17 @@ var loadModule_int32_code =
         cas1_i: do_cas1_i,
         cas2_i: do_cas2_i };
 `;
 
 var loadModule_int32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int32_code);
 
 function test_int32(heap) {
     var i32a = new SharedInt32Array(heap);
-    var i32m = asmLink(loadModule_int32, this, {}, heap);
+    var i32m = asmLink(loadModule_int32, atomicStdlib, {}, heap);
 
     var size = SharedInt32Array.BYTES_PER_ELEMENT;
 
     i32m.fence();
 
     i32a[0] = 12345;
     assertEq(i32m.load(), 12345);
     assertEq(i32m.load_i(size*0), 12345);
@@ -333,17 +354,17 @@ var loadModule_uint32_code =
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
-    var i32a = new stdlib.SharedUint32Array(heap);
+    var i32a = new stdlib.Uint32Array(heap);
 
     // Load element 0
     function do_load() {
         var v = 0;
         v = atomic_load(i32a, 0);
         return +(v>>>0);
     }
 
@@ -511,17 +532,17 @@ var loadModule_uint32_code =
         cas1_i: do_cas1_i,
         cas2_i: do_cas2_i };
 `;
 
 var loadModule_uint32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint32_code);
 
 function test_uint32(heap) {
     var i32a = new SharedUint32Array(heap);
-    var i32m = loadModule_uint32(this, {}, heap);
+    var i32m = loadModule_uint32(atomicStdlib, {}, heap);
 
     var size = SharedUint32Array.BYTES_PER_ELEMENT;
 
     i32a[0] = 12345;
     assertEq(i32m.load(), 12345);
     assertEq(i32m.load_i(size*0), 12345);
 
     assertEq(i32m.store(), 37);
@@ -614,17 +635,17 @@ var loadModule_int16_code =
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
-    var i16a = new stdlib.SharedInt16Array(heap);
+    var i16a = new stdlib.Int16Array(heap);
 
     function do_fence() {
         atomic_fence();
     }
 
     // Load element 0
     function do_load() {
         var v = 0;
@@ -797,17 +818,17 @@ var loadModule_int16_code =
         cas1_i: do_cas1_i,
         cas2_i: do_cas2_i };
 `
 
 var loadModule_int16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int16_code);
 
 function test_int16(heap) {
     var i16a = new SharedInt16Array(heap);
-    var i16m = loadModule_int16(this, {}, heap);
+    var i16m = loadModule_int16(atomicStdlib, {}, heap);
 
     var size = SharedInt16Array.BYTES_PER_ELEMENT;
 
     i16m.fence();
 
     i16a[0] = 12345;
     assertEq(i16m.load(), 12345);
     assertEq(i16m.load_i(size*0), 12345);
@@ -909,17 +930,17 @@ var loadModule_uint16_code =
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
-    var i16a = new stdlib.SharedUint16Array(heap);
+    var i16a = new stdlib.Uint16Array(heap);
 
     // Load element 0
     function do_load() {
         var v = 0;
         v = atomic_load(i16a, 0);
         return v|0;
     }
 
@@ -1087,17 +1108,17 @@ var loadModule_uint16_code =
         cas1_i: do_cas1_i,
         cas2_i: do_cas2_i };
 `
 
 var loadModule_uint16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint16_code);
 
 function test_uint16(heap) {
     var i16a = new SharedUint16Array(heap);
-    var i16m = loadModule_uint16(this, {}, heap);
+    var i16m = loadModule_uint16(atomicStdlib, {}, heap);
 
     var size = SharedUint16Array.BYTES_PER_ELEMENT;
 
     i16a[0] = 12345;
     assertEq(i16m.load(), 12345);
     assertEq(i16m.load_i(size*0), 12345);
 
     i16a[0] = -38;
@@ -1197,17 +1218,17 @@ var loadModule_int8_code =
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
-    var i8a = new stdlib.SharedInt8Array(heap);
+    var i8a = new stdlib.Int8Array(heap);
 
     // Load element 0
     function do_load() {
         var v = 0;
         v = atomic_load(i8a, 0);
         return v|0;
     }
 
@@ -1375,17 +1396,17 @@ var loadModule_int8_code =
         cas1_i: do_cas1_i,
         cas2_i: do_cas2_i };
 `
 
 var loadModule_int8 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int8_code);
 
 function test_int8(heap) {
     var i8a = new SharedInt8Array(heap);
-    var i8m = loadModule_int8(this, {}, heap);
+    var i8m = loadModule_int8(atomicStdlib, {}, heap);
 
     for ( var i=0 ; i < i8a.length ; i++ )
 	i8a[i] = 0;
 
     var size = SharedInt8Array.BYTES_PER_ELEMENT;
 
     i8a[0] = 123;
     assertEq(i8m.load(), 123);
@@ -1478,17 +1499,17 @@ var loadModule_uint8_code =
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
-    var i8a = new stdlib.SharedUint8Array(heap);
+    var i8a = new stdlib.Uint8Array(heap);
 
     // Load element 0
     function do_load() {
         var v = 0;
         v = atomic_load(i8a, 0);
         return v|0;
     }
 
@@ -1656,17 +1677,17 @@ var loadModule_uint8_code =
         cas1_i: do_cas1_i,
         cas2_i: do_cas2_i };
 `
 
 var loadModule_uint8 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint8_code);
 
 function test_uint8(heap) {
     var i8a = new SharedUint8Array(heap);
-    var i8m = loadModule_uint8(this, {}, heap);
+    var i8m = loadModule_uint8(atomicStdlib, {}, heap);
 
     for ( var i=0 ; i < i8a.length ; i++ )
 	i8a[i] = 0;
 
     var size = SharedUint8Array.BYTES_PER_ELEMENT;
 
     i8a[0] = 123;
     assertEq(i8m.load(), 123);
@@ -1811,17 +1832,17 @@ var loadModule_misc_code =
         ilf7: ilf7,
         ilf8: ilf8,
         ilf9: ilf9 };
 `
 
 var loadModule_misc = asmCompile('stdlib', 'foreign', 'heap', loadModule_misc_code);
 
 function test_misc(heap) {
-    var misc = loadModule_misc(this, {}, heap);
+    var misc = loadModule_misc(atomicStdlib, {}, heap);
 
     assertEq(misc.ilf1(), 1);
     assertEq(misc.ilf2(), 1);
     assertEq(misc.ilf3(), 0);
     assertEq(misc.ilf4(), 1);
     assertEq(misc.ilf5(), 0);
     assertEq(misc.ilf6(), 0);
     assertEq(misc.ilf7(), 0);
@@ -1843,17 +1864,17 @@ test_uint32(heap);
 test_misc(heap);
 
 // Test that ARM callouts compile.
 setARMHwCapFlags('vfp');
 
 asmCompile('stdlib', 'ffi', 'heap',
     USE_ASM + `
     var atomic_exchange = stdlib.Atomics.exchange;
-    var i8a = new stdlib.SharedInt8Array(heap);
+    var i8a = new stdlib.Int8Array(heap);
 
     function do_xchg() {
         var v = 0;
         v = atomic_exchange(i8a, 200, 37);
         return v|0;
     }
 
     return { xchg: do_xchg }