diff --git a/js/src/asmjs/AsmJSLink.cpp b/js/src/asmjs/AsmJSLink.cpp index f1cf7a1bb6a2..e7e5e4dc33d0 100644 --- a/js/src/asmjs/AsmJSLink.cpp +++ b/js/src/asmjs/AsmJSLink.cpp @@ -230,7 +230,7 @@ ValidateArrayView(JSContext* cx, AsmJSModule::Global& global, HandleValue global 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; diff --git a/js/src/asmjs/AsmJSModule.h b/js/src/asmjs/AsmJSModule.h index 88c9093a9d62..1afc0661414f 100644 --- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -314,6 +314,10 @@ class AsmJSModule 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_; @@ -1106,6 +1110,15 @@ class AsmJSModule 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(); + } + } /*************************************************************************/ diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index a92ca070420b..ac99f9b99a97 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -773,6 +773,10 @@ class MOZ_STACK_CLASS ModuleValidator MOZ_ASSERT(isAnyArrayView()); return u.viewInfo.isSharedView_; } + void setViewIsSharedView() { + MOZ_ASSERT(isAnyArrayView()); + u.viewInfo.isSharedView_ = true; + } bool isMathFunction() const { return which_ == MathBuiltinFunction; } @@ -918,6 +922,7 @@ class MOZ_STACK_CLASS ModuleValidator bool canValidateChangeHeap_; bool hasChangeHeap_; bool supportsSimd_; + bool atomicsPresent_; ScopedJSDeletePtr compileResults_; DebugOnly finishedFunctionBodies_; @@ -943,6 +948,7 @@ class MOZ_STACK_CLASS ModuleValidator canValidateChangeHeap_(false), hasChangeHeap_(false), supportsSimd_(cx->jitSupportsSimd()), + atomicsPresent_(false), compileResults_(nullptr), finishedFunctionBodies_(false) { @@ -1155,6 +1161,7 @@ class MOZ_STACK_CLASS ModuleValidator Global* global = moduleLifo_.new_(Global::AtomicsBuiltinFunction); if (!global) return false; + atomicsPresent_ = true; global->u.atomicsBuiltinFunc_ = func; return globals_.putNew(varName, global); } @@ -1509,6 +1516,14 @@ class MOZ_STACK_CLASS ModuleValidator } 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* compileResults) { diff --git a/js/src/jit-test/tests/asm.js/sta-transition.js b/js/src/jit-test/tests/asm.js/sta-transition.js new file mode 100644 index 000000000000..46c15368de83 --- /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); diff --git a/js/src/jit-test/tests/asm.js/testAtomics.js b/js/src/jit-test/tests/asm.js/testAtomics.js index 4f89a3bbc58a..931e4c00823f 100644 --- a/js/src/jit-test/tests/asm.js/testAtomics.js +++ b/js/src/jit-test/tests/asm.js/testAtomics.js @@ -1,5 +1,5 @@ // |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 @@ -8,6 +8,27 @@ if (!this.SharedArrayBuffer || !this.SharedInt32Array || !this.Atomics) 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; @@ -21,7 +42,7 @@ var loadModule_int32_code = 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(); @@ -233,7 +254,7 @@ var loadModule_int32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int32_ 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; @@ -338,7 +359,7 @@ var loadModule_uint32_code = 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() { @@ -516,7 +537,7 @@ var loadModule_uint32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint3 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; @@ -619,7 +640,7 @@ var loadModule_int16_code = 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(); @@ -802,7 +823,7 @@ var loadModule_int16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int16_ 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; @@ -914,7 +935,7 @@ var loadModule_uint16_code = 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() { @@ -1092,7 +1113,7 @@ var loadModule_uint16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint1 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; @@ -1202,7 +1223,7 @@ var loadModule_int8_code = 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() { @@ -1380,7 +1401,7 @@ var loadModule_int8 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int8_co 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; @@ -1483,7 +1504,7 @@ var loadModule_uint8_code = 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() { @@ -1661,7 +1682,7 @@ var loadModule_uint8 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint8_ 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; @@ -1816,7 +1837,7 @@ var loadModule_misc_code = 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); @@ -1848,7 +1869,7 @@ 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;