summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/atomics/basic-tests.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/atomics/basic-tests.js')
-rw-r--r--js/src/jit-test/tests/atomics/basic-tests.js564
1 files changed, 564 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js
new file mode 100644
index 0000000000..3712dd1b6f
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -0,0 +1,564 @@
+// Basic functional tests for the Atomics primitives.
+//
+// These do not test atomicity, just that calling and coercions and
+// indexing and exception behavior all work right.
+//
+// These do not test the wait/wake operations.
+
+load(libdir + "asserts.js");
+
+var DEBUG = false; // Set to true for useful printouts
+
+function dprint(...xs) {
+ if (!DEBUG)
+ return;
+ var s = "";
+ for ( var x in xs )
+ s += String(xs[x]);
+ print(s);
+}
+
+// Clone a function so that we get reliable inlining of primitives with --ion-eager.
+// For eg testMethod and testFunction that are polymorphic in the array a,
+// the inliner gets confused and stops inlining after Int8 -- not what we want.
+function CLONE(f) {
+ return this.eval("(" + f.toString() + ")");
+}
+
+function testMethod(a, ...indices) {
+ dprint("Method: " + a.constructor.name);
+ var poison;
+ switch (a.BYTES_PER_ELEMENT) {
+ case 1: poison = 0x5A; break;
+ case 2: poison = 0x5A5A; break;
+ case 4: poison = 0x5A5A5A5A; break;
+ }
+ for ( var i=0 ; i < indices.length ; i++ ) {
+ var x = indices[i];
+ if (x > 0)
+ a[x-1] = poison;
+ if (x < a.length-1)
+ a[x+1] = poison;
+
+ // val = 0
+ assertEq(Atomics.compareExchange(a, x, 0, 37), 0);
+ // val = 37
+ assertEq(Atomics.compareExchange(a, x, 37, 5), 37);
+ // val = 5
+ assertEq(Atomics.compareExchange(a, x, 7, 8), 5); // ie should fail
+ // val = 5
+ assertEq(Atomics.compareExchange(a, x, 5, 9), 5);
+ // val = 9
+ assertEq(Atomics.compareExchange(a, x, 5, 0), 9); // should also fail
+
+ // val = 9
+ assertEq(Atomics.exchange(a, x, 4), 9);
+ // val = 4
+ assertEq(Atomics.exchange(a, x, 9), 4);
+
+ // val = 9
+ assertEq(Atomics.load(a, x), 9);
+ // val = 9
+ assertEq(Atomics.store(a, x, 14), 14); // What about coercion?
+ // val = 14
+ assertEq(Atomics.load(a, x), 14);
+ // val = 14
+ Atomics.store(a, x, 0);
+ // val = 0
+
+ // val = 0
+ assertEq(Atomics.add(a, x, 3), 0);
+ // val = 3
+ assertEq(Atomics.sub(a, x, 2), 3);
+ // val = 1
+ assertEq(Atomics.or(a, x, 6), 1);
+ // val = 7
+ assertEq(Atomics.and(a, x, 14), 7);
+ // val = 6
+ assertEq(Atomics.xor(a, x, 5), 6);
+ // val = 3
+ assertEq(Atomics.load(a, x), 3);
+ // val = 3
+ Atomics.store(a, x, 0);
+ // val = 0
+
+ // Check adjacent elements were not affected
+ if (x > 0) {
+ assertEq(a[x-1], poison);
+ a[x-1] = 0;
+ }
+ if (x < a.length-1) {
+ assertEq(a[x+1], poison);
+ a[x+1] = 0;
+ }
+ }
+}
+
+function testFunction(a, ...indices) {
+ dprint("Function: " + a.constructor.name);
+ var poison;
+ switch (a.BYTES_PER_ELEMENT) {
+ case 1: poison = 0x5A; break;
+ case 2: poison = 0x5A5A; break;
+ case 4: poison = 0x5A5A5A5A; break;
+ }
+ for ( var i=0 ; i < indices.length ; i++ ) {
+ var x = indices[i];
+ if (x > 0)
+ a[x-1] = poison;
+ if (x < a.length-1)
+ a[x+1] = poison;
+
+ // val = 0
+ assertEq(gAtomics_compareExchange(a, x, 0, 37), 0);
+ // val = 37
+ assertEq(gAtomics_compareExchange(a, x, 37, 5), 37);
+ // val = 5
+ assertEq(gAtomics_compareExchange(a, x, 7, 8), 5); // ie should fail
+ // val = 5
+ assertEq(gAtomics_compareExchange(a, x, 5, 9), 5);
+ // val = 9
+ assertEq(gAtomics_compareExchange(a, x, 5, 0), 9); // should also fail
+
+ // val = 9
+ assertEq(gAtomics_exchange(a, x, 4), 9);
+ // val = 4
+ assertEq(gAtomics_exchange(a, x, 9), 4);
+
+ // val = 9
+ assertEq(gAtomics_load(a, x), 9);
+ // val = 9
+ assertEq(gAtomics_store(a, x, 14), 14); // What about coercion?
+ // val = 14
+ assertEq(gAtomics_load(a, x), 14);
+ // val = 14
+ gAtomics_store(a, x, 0);
+ // val = 0
+
+ // val = 0
+ assertEq(gAtomics_add(a, x, 3), 0);
+ // val = 3
+ assertEq(gAtomics_sub(a, x, 2), 3);
+ // val = 1
+ assertEq(gAtomics_or(a, x, 6), 1);
+ // val = 7
+ assertEq(gAtomics_and(a, x, 14), 7);
+ // val = 6
+ assertEq(gAtomics_xor(a, x, 5), 6);
+ // val = 3
+ assertEq(gAtomics_load(a, x), 3);
+ // val = 3
+ gAtomics_store(a, x, 0);
+ // val = 0
+
+ // Check adjacent elements were not affected
+ if (x > 0) {
+ assertEq(a[x-1], poison);
+ a[x-1] = 0;
+ }
+ if (x < a.length-1) {
+ assertEq(a[x+1], poison);
+ a[x+1] = 0;
+ }
+ }
+}
+
+function testTypeCAS(a) {
+ dprint("Type: " + a.constructor.name);
+
+ var thrown = false;
+ try {
+ Atomics.compareExchange([0], 0, 0, 1);
+ }
+ catch (e) {
+ thrown = true;
+ assertEq(e instanceof TypeError, true);
+ }
+ assertEq(thrown, true);
+
+ // All these variants should be OK
+ Atomics.compareExchange(a, 0, 0.7, 1.8);
+ Atomics.compareExchange(a, 0, "0", 1);
+ Atomics.compareExchange(a, 0, 0, "1");
+ Atomics.compareExchange(a, 0, 0);
+}
+
+function testTypeBinop(a, op) {
+ dprint("Type: " + a.constructor.name);
+
+ var thrown = false;
+ try {
+ op([0], 0, 1);
+ }
+ catch (e) {
+ thrown = true;
+ assertEq(e instanceof TypeError, true);
+ }
+ assertEq(thrown, true);
+
+ // These are all OK
+ op(a, 0, 0.7);
+ op(a, 0, "0");
+ op(a, 0);
+}
+
+var globlength = 0; // Will be set later
+
+function testRangeCAS(a) {
+ dprint("Range: " + a.constructor.name);
+
+ var msg = /out-of-range index/; // A generic message
+
+ assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg);
+ assertEq(a[0], 0);
+
+ // Converted to 0
+ assertEq(Atomics.compareExchange(a, "hi", 0, 33), 0);
+ assertEq(a[0], 33);
+ a[0] = 0;
+
+ assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg);
+ assertEq(a[0], 0);
+
+ assertErrorMessage(() => Atomics.compareExchange(a, globlength, 0, 1), RangeError, msg);
+ assertEq(a[0], 0);
+}
+
+// Ad-hoc tests for extreme and out-of-range values.
+// None of these should throw
+
+function testInt8Extremes(a) {
+ dprint("Int8 extremes");
+
+ a[10] = 0;
+ a[11] = 0;
+
+ Atomics.store(a, 10, 255);
+ assertEq(a[10], -1);
+ assertEq(Atomics.load(a, 10), -1);
+
+ Atomics.add(a, 10, 255); // should coerce to -1
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.add(a, 10, -1);
+ assertEq(a[10], -3);
+ assertEq(Atomics.load(a, 10), -3);
+
+ Atomics.sub(a, 10, 255); // should coerce to -1
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.sub(a, 10, 256); // should coerce to 0
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.and(a, 10, -1); // Preserve all
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.and(a, 10, 256); // Preserve none
+ assertEq(a[10], 0);
+ assertEq(Atomics.load(a, 10), 0);
+
+ Atomics.store(a, 10, 255);
+ assertEq(Atomics.exchange(a, 10, 0), -1);
+
+ assertEq(a[11], 0);
+}
+
+function testUint8Extremes(a) {
+ dprint("Uint8 extremes");
+
+ a[10] = 0;
+ a[11] = 0;
+
+ Atomics.store(a, 10, 255);
+ assertEq(a[10], 255);
+ assertEq(Atomics.load(a, 10), 255);
+
+ Atomics.add(a, 10, 255);
+ assertEq(a[10], 254);
+ assertEq(Atomics.load(a, 10), 254);
+
+ Atomics.add(a, 10, -1);
+ assertEq(a[10], 253);
+ assertEq(Atomics.load(a, 10), 253);
+
+ Atomics.sub(a, 10, 255);
+ assertEq(a[10], 254);
+ assertEq(Atomics.load(a, 10), 254);
+
+ Atomics.and(a, 10, -1); // Preserve all
+ assertEq(a[10], 254);
+ assertEq(Atomics.load(a, 10), 254);
+
+ Atomics.and(a, 10, 256); // Preserve none
+ assertEq(a[10], 0);
+ assertEq(Atomics.load(a, 10), 0);
+
+ Atomics.store(a, 10, 255);
+ assertEq(Atomics.exchange(a, 10, 0), 255);
+
+ assertEq(a[11], 0);
+}
+
+function testInt16Extremes(a) {
+ dprint("Int16 extremes");
+
+ a[10] = 0;
+ a[11] = 0;
+
+ Atomics.store(a, 10, 65535);
+ assertEq(a[10], -1);
+ assertEq(Atomics.load(a, 10), -1);
+
+ Atomics.add(a, 10, 65535); // should coerce to -1
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.add(a, 10, -1);
+ assertEq(a[10], -3);
+ assertEq(Atomics.load(a, 10), -3);
+
+ Atomics.sub(a, 10, 65535); // should coerce to -1
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.sub(a, 10, 65536); // should coerce to 0
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.and(a, 10, -1); // Preserve all
+ assertEq(a[10], -2);
+ assertEq(Atomics.load(a, 10), -2);
+
+ Atomics.and(a, 10, 65536); // Preserve none
+ assertEq(a[10], 0);
+ assertEq(Atomics.load(a, 10), 0);
+
+ assertEq(a[11], 0);
+}
+
+function testUint32(a) {
+ var k = 0;
+ for ( var i=0 ; i < 20 ; i++ ) {
+ a[i] = i+5;
+ k += a[i];
+ }
+
+ var sum = 0;
+ for ( var i=0 ; i < 20 ; i++ )
+ sum += Atomics.add(a, i, 1);
+
+ assertEq(sum, k);
+}
+
+// This test is a reliable test of sign extension in the JIT where
+// testInt8Extremes is not (because there may not be enough type
+// information without a loop - see bug 1181062 for a description
+// of the general problem).
+
+function exchangeLoop(ta) {
+ var sum = 0;
+ for ( var i=0 ; i < 100000 ; i++ )
+ sum += Atomics.exchange(ta, i & 15, 255);
+ return sum;
+}
+
+function adHocExchange(SharedOrUnsharedArrayBuffer) {
+ var a = new Int8Array(new SharedOrUnsharedArrayBuffer(16));
+ for ( var i=0 ; i < a.length ; i++ )
+ a[i] = 255;
+ assertEq(exchangeLoop(a), -100000);
+}
+
+// isLockFree(n) may return true only if there is an integer array
+// on which atomic operations is allowed whose byte size is n,
+// ie, it must return false for n=7.
+//
+// SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(8), isLockFree(4)
+// on all supported platforms, only the last is guaranteed by the spec.
+
+var sizes = [ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12];
+var answers = [ true, true, false, true, false, false, false, true,
+ false, false, false, false];
+
+function testIsLockFree() {
+ // This ought to defeat most compile-time resolution.
+ for ( var i=0 ; i < sizes.length ; i++ ) {
+ var v = Atomics.isLockFree(sizes[i]);
+ var a = answers[i];
+ assertEq(typeof v, 'boolean');
+ assertEq(v, a);
+ }
+
+ // This ought to be optimizable.
+ assertEq(Atomics.isLockFree(1), true);
+ assertEq(Atomics.isLockFree(2), true);
+ assertEq(Atomics.isLockFree(3), false);
+ assertEq(Atomics.isLockFree(4), true);
+ assertEq(Atomics.isLockFree(5), false);
+ assertEq(Atomics.isLockFree(6), false);
+ assertEq(Atomics.isLockFree(7), false);
+ assertEq(Atomics.isLockFree(8), true);
+ assertEq(Atomics.isLockFree(9), false);
+ assertEq(Atomics.isLockFree(10), false);
+ assertEq(Atomics.isLockFree(11), false);
+ assertEq(Atomics.isLockFree(12), false);
+}
+
+function testIsLockFree2() {
+ assertEq(Atomics.isLockFree(0), false);
+ assertEq(Atomics.isLockFree(0/-1), false);
+ assertEq(Atomics.isLockFree(3.5), false);
+ assertEq(Atomics.isLockFree(Number.NaN), false); // NaN => +0
+ assertEq(Atomics.isLockFree(Number.POSITIVE_INFINITY), false);
+ assertEq(Atomics.isLockFree(Number.NEGATIVE_INFINITY), false);
+ assertEq(Atomics.isLockFree(-4), false);
+ assertEq(Atomics.isLockFree('4'), true);
+ assertEq(Atomics.isLockFree('-4'), false);
+ assertEq(Atomics.isLockFree('4.5'), true);
+ assertEq(Atomics.isLockFree('5.5'), false);
+ assertEq(Atomics.isLockFree(new Number(4)), true);
+ assertEq(Atomics.isLockFree(new String('4')), true);
+ assertEq(Atomics.isLockFree(new Boolean(true)), true);
+ var thrown = false;
+ try {
+ Atomics.isLockFree(Symbol('1'));
+ } catch (e) {
+ thrown = e;
+ }
+ assertEq(thrown instanceof TypeError, true);
+ assertEq(Atomics.isLockFree(true), true);
+ assertEq(Atomics.isLockFree(false), false);
+ assertEq(Atomics.isLockFree(undefined), false);
+ assertEq(Atomics.isLockFree(null), false);
+ assertEq(Atomics.isLockFree({toString: () => '4'}), true);
+ assertEq(Atomics.isLockFree({valueOf: () => 4}), true);
+ assertEq(Atomics.isLockFree({valueOf: () => 5}), false);
+ assertEq(Atomics.isLockFree({password: "qumquat"}), false);
+}
+
+function testUint8Clamped(sab) {
+ var ta = new Uint8ClampedArray(sab);
+ var thrown = false;
+ try {
+ CLONE(testMethod)(ta, 0);
+ }
+ catch (e) {
+ thrown = true;
+ assertEq(e instanceof TypeError, true);
+ }
+ assertEq(thrown, true);
+}
+
+function testWeirdIndices(SharedOrUnsharedArrayBuffer) {
+ var a = new Int8Array(new SharedOrUnsharedArrayBuffer(16));
+ a[3] = 10;
+ assertEq(Atomics.load(a, "0x03"), 10);
+ assertEq(Atomics.load(a, {valueOf: () => 3}), 10);
+}
+
+function isLittleEndian() {
+ var xxx = new ArrayBuffer(2);
+ var xxa = new Int16Array(xxx);
+ var xxb = new Int8Array(xxx);
+ xxa[0] = 37;
+ var is_little = xxb[0] == 37;
+ return is_little;
+}
+
+function runTests(SharedOrUnsharedArrayBuffer) {
+ var is_little = isLittleEndian();
+
+ // Currently the SharedArrayBuffer needs to be a multiple of 4K bytes in size.
+ var sab = new SharedOrUnsharedArrayBuffer(4096);
+
+ // Test that two arrays created on the same storage alias
+ var t1 = new Int8Array(sab);
+ var t2 = new Uint16Array(sab);
+
+ assertEq(t1[0], 0);
+ assertEq(t2[0], 0);
+ t1[0] = 37;
+ if (is_little)
+ assertEq(t2[0], 37);
+ else
+ assertEq(t2[0], 37 << 8);
+ t1[0] = 0;
+
+ // Test that invoking as Atomics.whatever() works, on correct arguments.
+ CLONE(testMethod)(new Int8Array(sab), 0, 42, 4095);
+ CLONE(testMethod)(new Uint8Array(sab), 0, 42, 4095);
+ CLONE(testMethod)(new Int16Array(sab), 0, 42, 2047);
+ CLONE(testMethod)(new Uint16Array(sab), 0, 42, 2047);
+ CLONE(testMethod)(new Int32Array(sab), 0, 42, 1023);
+ CLONE(testMethod)(new Uint32Array(sab), 0, 42, 1023);
+
+ // Test that invoking as v = Atomics.whatever; v() works, on correct arguments.
+ gAtomics_compareExchange = Atomics.compareExchange;
+ gAtomics_exchange = Atomics.exchange;
+ gAtomics_load = Atomics.load;
+ gAtomics_store = Atomics.store;
+ gAtomics_add = Atomics.add;
+ gAtomics_sub = Atomics.sub;
+ gAtomics_and = Atomics.and;
+ gAtomics_or = Atomics.or;
+ gAtomics_xor = Atomics.xor;
+
+ CLONE(testFunction)(new Int8Array(sab), 0, 42, 4095);
+ CLONE(testFunction)(new Uint8Array(sab), 0, 42, 4095);
+ CLONE(testFunction)(new Int16Array(sab), 0, 42, 2047);
+ CLONE(testFunction)(new Uint16Array(sab), 0, 42, 2047);
+ CLONE(testFunction)(new Int32Array(sab), 0, 42, 1023);
+ CLONE(testFunction)(new Uint32Array(sab), 0, 42, 1023);
+
+ // Test various range and type conditions
+ var v8 = new Int8Array(sab);
+ var v32 = new Int32Array(sab);
+
+ CLONE(testTypeCAS)(v8);
+ CLONE(testTypeCAS)(v32);
+
+ CLONE(testTypeBinop)(v8, Atomics.add);
+ CLONE(testTypeBinop)(v8, Atomics.sub);
+ CLONE(testTypeBinop)(v8, Atomics.and);
+ CLONE(testTypeBinop)(v8, Atomics.or);
+ CLONE(testTypeBinop)(v8, Atomics.xor);
+
+ CLONE(testTypeBinop)(v32, Atomics.add);
+ CLONE(testTypeBinop)(v32, Atomics.sub);
+ CLONE(testTypeBinop)(v32, Atomics.and);
+ CLONE(testTypeBinop)(v32, Atomics.or);
+ CLONE(testTypeBinop)(v32, Atomics.xor);
+
+ // Test out-of-range references
+ globlength = v8.length + 5;
+ CLONE(testRangeCAS)(v8);
+ globlength = v32.length + 5;
+ CLONE(testRangeCAS)(v32);
+
+ // Test extreme values
+ testInt8Extremes(new Int8Array(sab));
+ testUint8Extremes(new Uint8Array(sab));
+ testInt16Extremes(new Int16Array(sab));
+ testUint32(new Uint32Array(sab));
+
+ // Test that Uint8ClampedArray is not accepted.
+ testUint8Clamped(sab);
+
+ // Misc ad-hoc tests
+ adHocExchange(SharedOrUnsharedArrayBuffer);
+
+ // Misc
+ testIsLockFree();
+ testIsLockFree2();
+ testWeirdIndices(SharedOrUnsharedArrayBuffer);
+
+ assertEq(Atomics[Symbol.toStringTag], "Atomics");
+}
+
+runTests(SharedArrayBuffer);
+runTests(ArrayBuffer);