summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/atomics
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/atomics')
-rw-r--r--js/src/jit-test/tests/atomics/basic-tests.js564
-rw-r--r--js/src/jit-test/tests/atomics/bigint-add-for-effect.js103
-rw-r--r--js/src/jit-test/tests/atomics/bigint-add.js103
-rw-r--r--js/src/jit-test/tests/atomics/bigint-and-for-effect.js128
-rw-r--r--js/src/jit-test/tests/atomics/bigint-and.js128
-rw-r--r--js/src/jit-test/tests/atomics/bigint-compareExchange.js121
-rw-r--r--js/src/jit-test/tests/atomics/bigint-exchange.js103
-rw-r--r--js/src/jit-test/tests/atomics/bigint-load.js91
-rw-r--r--js/src/jit-test/tests/atomics/bigint-or-for-effect.js130
-rw-r--r--js/src/jit-test/tests/atomics/bigint-or.js130
-rw-r--r--js/src/jit-test/tests/atomics/bigint-store.js91
-rw-r--r--js/src/jit-test/tests/atomics/bigint-sub-for-effect.js103
-rw-r--r--js/src/jit-test/tests/atomics/bigint-sub.js103
-rw-r--r--js/src/jit-test/tests/atomics/bigint-xor-for-effect.js121
-rw-r--r--js/src/jit-test/tests/atomics/bigint-xor.js121
-rw-r--r--js/src/jit-test/tests/atomics/cross-compartment-nukeccw.js219
-rw-r--r--js/src/jit-test/tests/atomics/directives.txt1
-rw-r--r--js/src/jit-test/tests/atomics/inline-add.js28
-rw-r--r--js/src/jit-test/tests/atomics/inline-add2.js28
-rw-r--r--js/src/jit-test/tests/atomics/inline-cmpxchg.js28
-rw-r--r--js/src/jit-test/tests/atomics/memcpy-fidelity.js181
-rw-r--r--js/src/jit-test/tests/atomics/mutual-exclusion.js82
-rw-r--r--js/src/jit-test/tests/atomics/nursery-non-shared-moved-gc.js99
-rw-r--r--js/src/jit-test/tests/atomics/optimization-tests.js103
-rw-r--r--js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js44
25 files changed, 2953 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);
diff --git a/js/src/jit-test/tests/atomics/bigint-add-for-effect.js b/js/src/jit-test/tests/atomics/bigint-add-for-effect.js
new file mode 100644
index 0000000000..bed65c6aae
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-add-for-effect.js
@@ -0,0 +1,103 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testAdd() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ Atomics.add(int64, 0, value);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ Atomics.add(uint64, 0, value);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ Atomics.add(int64, 0, -value);
+ assertEq(int64[0], 0n);
+
+ Atomics.add(uint64, 0, -value);
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ Atomics.add(int64, idx, value);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ Atomics.add(uint64, idx, value);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ Atomics.add(int64, idx, -value);
+ assertEq(int64[idx], 0n);
+
+ Atomics.add(uint64, idx, -value);
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testAdd();
diff --git a/js/src/jit-test/tests/atomics/bigint-add.js b/js/src/jit-test/tests/atomics/bigint-add.js
new file mode 100644
index 0000000000..18c97b4214
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-add.js
@@ -0,0 +1,103 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testAdd() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ assertEq(Atomics.add(int64, 0, value), 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.add(uint64, 0, value), 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ assertEq(Atomics.add(int64, 0, -value), BigInt.asIntN(64, value));
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.add(uint64, 0, -value), BigInt.asUintN(64, value));
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ assertEq(Atomics.add(int64, idx, value), 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.add(uint64, idx, value), 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ assertEq(Atomics.add(int64, idx, -value), BigInt.asIntN(64, value));
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.add(uint64, idx, -value), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testAdd();
diff --git a/js/src/jit-test/tests/atomics/bigint-and-for-effect.js b/js/src/jit-test/tests/atomics/bigint-and-for-effect.js
new file mode 100644
index 0000000000..cbe3c7e065
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-and-for-effect.js
@@ -0,0 +1,128 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testAnd() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ const int64All = -1n;
+ const uint64All = 0xffff_ffff_ffff_ffffn;
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // x & 0 == 0
+ Atomics.and(int64, 0, value);
+ assertEq(int64[0], 0n);
+
+ Atomics.and(uint64, 0, value);
+ assertEq(uint64[0], 0n);
+
+ // x & -1 == x
+ int64[0] = int64All;
+ Atomics.and(int64, 0, value);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ uint64[0] = uint64All;
+ Atomics.and(uint64, 0, value);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // 0 & x == 0
+ Atomics.and(int64, 0, 0n);
+ assertEq(int64[0], 0n);
+
+ Atomics.and(uint64, 0, 0n);
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // x & 0 == 0
+ Atomics.and(int64, idx, value);
+ assertEq(int64[idx], 0n);
+
+ Atomics.and(uint64, idx, value);
+ assertEq(uint64[idx], 0n);
+
+ // x & -1 == x
+ int64[idx] = int64All;
+ Atomics.and(int64, idx, value);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ uint64[idx] = uint64All;
+ Atomics.and(uint64, idx, value);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // 0 & x == 0
+ Atomics.and(int64, idx, 0n);
+ assertEq(int64[idx], 0n);
+
+ Atomics.and(uint64, idx, 0n);
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testAnd();
diff --git a/js/src/jit-test/tests/atomics/bigint-and.js b/js/src/jit-test/tests/atomics/bigint-and.js
new file mode 100644
index 0000000000..b80535e6aa
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-and.js
@@ -0,0 +1,128 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testAnd() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ const int64All = -1n;
+ const uint64All = 0xffff_ffff_ffff_ffffn;
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // x & 0 == 0
+ assertEq(Atomics.and(int64, 0, value), 0n);
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.and(uint64, 0, value), 0n);
+ assertEq(uint64[0], 0n);
+
+ // x & -1 == x
+ int64[0] = int64All;
+ assertEq(Atomics.and(int64, 0, value), int64All);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ uint64[0] = uint64All;
+ assertEq(Atomics.and(uint64, 0, value), uint64All);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // 0 & x == 0
+ assertEq(Atomics.and(int64, 0, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.and(uint64, 0, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // x & 0 == 0
+ assertEq(Atomics.and(int64, idx, value), 0n);
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.and(uint64, idx, value), 0n);
+ assertEq(uint64[idx], 0n);
+
+ // x & -1 == x
+ int64[idx] = int64All;
+ assertEq(Atomics.and(int64, idx, value), int64All);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ uint64[idx] = uint64All;
+ assertEq(Atomics.and(uint64, idx, value), uint64All);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // 0 & x == 0
+ assertEq(Atomics.and(int64, idx, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.and(uint64, idx, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testAnd();
diff --git a/js/src/jit-test/tests/atomics/bigint-compareExchange.js b/js/src/jit-test/tests/atomics/bigint-compareExchange.js
new file mode 100644
index 0000000000..b899ea9adc
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-compareExchange.js
@@ -0,0 +1,121 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testCompareExchange() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // Comparison fails.
+ assertEq(Atomics.compareExchange(int64, 0, 1n, value), 0n);
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.compareExchange(uint64, 0, 1n, value), 0n);
+ assertEq(uint64[0], 0n);
+
+ // Comparison succeeds, the new value is written into the memory location.
+ assertEq(Atomics.compareExchange(int64, 0, 0n, value), 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.compareExchange(uint64, 0, 0n, value), 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // Comparison succeeds, the memory location is reset.
+ assertEq(Atomics.compareExchange(int64, 0, value, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.compareExchange(uint64, 0, value, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // Comparison fails.
+ assertEq(Atomics.compareExchange(int64, idx, 1n, value), 0n);
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.compareExchange(uint64,idx, 1n, value), 0n);
+ assertEq(uint64[idx], 0n);
+
+ // Comparison succeeds, the new value is written into the memory location.
+ assertEq(Atomics.compareExchange(int64, idx, 0n, value), 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.compareExchange(uint64, idx, 0n, value), 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // Comparison succeeds, the memory location is reset.
+ assertEq(Atomics.compareExchange(int64, idx, value, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.compareExchange(uint64, idx, value, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testCompareExchange();
diff --git a/js/src/jit-test/tests/atomics/bigint-exchange.js b/js/src/jit-test/tests/atomics/bigint-exchange.js
new file mode 100644
index 0000000000..1d36167ca3
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-exchange.js
@@ -0,0 +1,103 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testExchange() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ assertEq(Atomics.exchange(int64, 0, value), 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.exchange(uint64, 0, value), 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ assertEq(Atomics.exchange(int64, 0, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.exchange(uint64, 0, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ assertEq(Atomics.exchange(int64, idx, value), 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.exchange(uint64, idx, value), 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ assertEq(Atomics.exchange(int64, idx, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.exchange(uint64, idx, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testExchange();
diff --git a/js/src/jit-test/tests/atomics/bigint-load.js b/js/src/jit-test/tests/atomics/bigint-load.js
new file mode 100644
index 0000000000..f16f3583d2
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-load.js
@@ -0,0 +1,91 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testLoad() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 50; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ int64[0] = value;
+ assertEq(Atomics.load(int64, 0), BigInt.asIntN(64, value));
+
+ uint64[0] = value;
+ assertEq(Atomics.load(uint64, 0), BigInt.asUintN(64, value));
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 50; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ int64[idx] = value;
+ assertEq(Atomics.load(int64, idx), BigInt.asIntN(64, value));
+
+ uint64[idx] = value;
+ assertEq(Atomics.load(uint64, idx), BigInt.asUintN(64, value));
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testLoad();
diff --git a/js/src/jit-test/tests/atomics/bigint-or-for-effect.js b/js/src/jit-test/tests/atomics/bigint-or-for-effect.js
new file mode 100644
index 0000000000..4d25f298b6
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-or-for-effect.js
@@ -0,0 +1,130 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testOr() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ const int64All = -1n;
+ const uint64All = 0xffff_ffff_ffff_ffffn;
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // x | 0 == x
+ Atomics.or(int64, 0, value);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ Atomics.or(uint64, 0, value);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // 0 | x == x
+ Atomics.or(int64, 0, 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ Atomics.or(uint64, 0, 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // x | ~x == -1
+ Atomics.or(int64, 0, ~value);
+ assertEq(int64[0], int64All);
+
+ Atomics.or(uint64, 0, ~value);
+ assertEq(uint64[0], uint64All);
+
+ int64[0] = 0n;
+ uint64[0] = 0n;
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // x | 0 == x
+ Atomics.or(int64, idx, value);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ Atomics.or(uint64, idx, value);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // 0 | x == x
+ Atomics.or(int64, idx, 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ Atomics.or(uint64, idx, 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // x | ~x == -1
+ Atomics.or(int64, idx, ~value);
+ assertEq(int64[idx], int64All);
+
+ Atomics.or(uint64, idx, ~value);
+ assertEq(uint64[idx], uint64All);
+
+ int64[idx] = 0n;
+ uint64[idx] = 0n;
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testOr();
diff --git a/js/src/jit-test/tests/atomics/bigint-or.js b/js/src/jit-test/tests/atomics/bigint-or.js
new file mode 100644
index 0000000000..95b99958e5
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-or.js
@@ -0,0 +1,130 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testOr() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ const int64All = -1n;
+ const uint64All = 0xffff_ffff_ffff_ffffn;
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // x | 0 == x
+ assertEq(Atomics.or(int64, 0, value), 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.or(uint64, 0, value), 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // 0 | x == x
+ assertEq(Atomics.or(int64, 0, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.or(uint64, 0, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // x | ~x == -1
+ assertEq(Atomics.or(int64, 0, ~value), BigInt.asIntN(64, value));
+ assertEq(int64[0], int64All);
+
+ assertEq(Atomics.or(uint64, 0, ~value), BigInt.asUintN(64, value));
+ assertEq(uint64[0], uint64All);
+
+ int64[0] = 0n;
+ uint64[0] = 0n;
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // x | 0 == x
+ assertEq(Atomics.or(int64, idx, value), 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.or(uint64, idx, value), 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // 0 | x == x
+ assertEq(Atomics.or(int64, idx, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.or(uint64, idx, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // x | ~x == -1
+ assertEq(Atomics.or(int64, idx, ~value), BigInt.asIntN(64, value));
+ assertEq(int64[idx], int64All);
+
+ assertEq(Atomics.or(uint64, idx, ~value), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], uint64All);
+
+ int64[idx] = 0n;
+ uint64[idx] = 0n;
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testOr();
diff --git a/js/src/jit-test/tests/atomics/bigint-store.js b/js/src/jit-test/tests/atomics/bigint-store.js
new file mode 100644
index 0000000000..1b38f8f087
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-store.js
@@ -0,0 +1,91 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testStore() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 50; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ assertEq(Atomics.store(int64, 0, value), value);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.store(uint64, 0, value), value);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 50; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ assertEq(Atomics.store(int64, idx, value), value);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.store(uint64, idx, value), value);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testStore();
diff --git a/js/src/jit-test/tests/atomics/bigint-sub-for-effect.js b/js/src/jit-test/tests/atomics/bigint-sub-for-effect.js
new file mode 100644
index 0000000000..5ca0a82fe8
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-sub-for-effect.js
@@ -0,0 +1,103 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testSub() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ Atomics.sub(int64, 0, value);
+ assertEq(int64[0], BigInt.asIntN(64, -value));
+
+ Atomics.sub(uint64, 0, value);
+ assertEq(uint64[0], BigInt.asUintN(64, -value));
+
+ Atomics.sub(int64, 0, -value);
+ assertEq(int64[0], 0n);
+
+ Atomics.sub(uint64, 0, -value);
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ Atomics.sub(int64, idx, value);
+ assertEq(int64[idx], BigInt.asIntN(64, -value));
+
+ Atomics.sub(uint64, idx, value);
+ assertEq(uint64[idx], BigInt.asUintN(64, -value));
+
+ Atomics.sub(int64, idx, -value);
+ assertEq(int64[idx], 0n);
+
+ Atomics.sub(uint64, idx, -value);
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testSub();
diff --git a/js/src/jit-test/tests/atomics/bigint-sub.js b/js/src/jit-test/tests/atomics/bigint-sub.js
new file mode 100644
index 0000000000..deb997617e
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-sub.js
@@ -0,0 +1,103 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testSub() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ assertEq(Atomics.sub(int64, 0, value), 0n);
+ assertEq(int64[0], BigInt.asIntN(64, -value));
+
+ assertEq(Atomics.sub(uint64, 0, value), 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, -value));
+
+ assertEq(Atomics.sub(int64, 0, -value), BigInt.asIntN(64, -value));
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.sub(uint64, 0, -value), BigInt.asUintN(64, -value));
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ assertEq(Atomics.sub(int64, idx, value), 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, -value));
+
+ assertEq(Atomics.sub(uint64, idx, value), 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, -value));
+
+ assertEq(Atomics.sub(int64, idx, -value), BigInt.asIntN(64, -value));
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.sub(uint64, idx, -value), BigInt.asUintN(64, -value));
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testSub();
diff --git a/js/src/jit-test/tests/atomics/bigint-xor-for-effect.js b/js/src/jit-test/tests/atomics/bigint-xor-for-effect.js
new file mode 100644
index 0000000000..0b70b07193
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-xor-for-effect.js
@@ -0,0 +1,121 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testXor() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // x ^ 0 == x
+ Atomics.xor(int64, 0, value);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ Atomics.xor(uint64, 0, value);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // 0 ^ x == x
+ Atomics.xor(int64, 0, 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ Atomics.xor(uint64, 0, 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // x ^ x == 0
+ Atomics.xor(int64, 0, value);
+ assertEq(int64[0], 0n);
+
+ Atomics.xor(uint64, 0, value);
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // x ^ 0 == x
+ Atomics.xor(int64, idx, value);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ Atomics.xor(uint64, idx, value);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // 0 ^ x == x
+ Atomics.xor(int64, idx, 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ Atomics.xor(uint64, idx, 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // x ^ x == 0
+ Atomics.xor(int64, idx, value);
+ assertEq(int64[idx], 0n);
+
+ Atomics.xor(uint64, idx, value);
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testXor();
diff --git a/js/src/jit-test/tests/atomics/bigint-xor.js b/js/src/jit-test/tests/atomics/bigint-xor.js
new file mode 100644
index 0000000000..9e565ffbcb
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/bigint-xor.js
@@ -0,0 +1,121 @@
+// |jit-test| test-join=--spectre-mitigations=off
+
+// These do not test atomicity, just that code generation for BigInt values
+// works correctly.
+
+const bigIntValues = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function testXor() {
+ const int64 = new BigInt64Array(2);
+ const uint64 = new BigUint64Array(2);
+
+ // Test with constant index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+
+ // x ^ 0 == x
+ assertEq(Atomics.xor(int64, 0, value), 0n);
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.xor(uint64, 0, value), 0n);
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // 0 ^ x == x
+ assertEq(Atomics.xor(int64, 0, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[0], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.xor(uint64, 0, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[0], BigInt.asUintN(64, value));
+
+ // x ^ x == 0
+ assertEq(Atomics.xor(int64, 0, value), BigInt.asIntN(64, value));
+ assertEq(int64[0], 0n);
+
+ assertEq(Atomics.xor(uint64, 0, value), BigInt.asUintN(64, value));
+ assertEq(uint64[0], 0n);
+ }
+ }
+
+ // Test with variable index.
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < bigIntValues.length; ++j) {
+ let value = bigIntValues[j];
+ let idx = j & 1;
+
+ // x ^ 0 == x
+ assertEq(Atomics.xor(int64, idx, value), 0n);
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.xor(uint64, idx, value), 0n);
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // 0 ^ x == x
+ assertEq(Atomics.xor(int64, idx, 0n), BigInt.asIntN(64, value));
+ assertEq(int64[idx], BigInt.asIntN(64, value));
+
+ assertEq(Atomics.xor(uint64, idx, 0n), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], BigInt.asUintN(64, value));
+
+ // x ^ x == 0
+ assertEq(Atomics.xor(int64, idx, value), BigInt.asIntN(64, value));
+ assertEq(int64[idx], 0n);
+
+ assertEq(Atomics.xor(uint64, idx, value), BigInt.asUintN(64, value));
+ assertEq(uint64[idx], 0n);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testXor();
diff --git a/js/src/jit-test/tests/atomics/cross-compartment-nukeccw.js b/js/src/jit-test/tests/atomics/cross-compartment-nukeccw.js
new file mode 100644
index 0000000000..2ef9030923
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/cross-compartment-nukeccw.js
@@ -0,0 +1,219 @@
+load(libdir + "asserts.js");
+
+const otherGlobal = newGlobal({newCompartment: true});
+
+function nukeValue(wrapper, value) {
+ return {
+ valueOf() {
+ nukeCCW(wrapper);
+ return value;
+ }
+ };
+};
+
+function assertIsDeadWrapper(value) {
+ assertTypeErrorMessage(() => value.foo, "can't access dead object");
+}
+
+// Atomics.load
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.load(ta, nukeValue(ta, 0));
+
+ assertEq(val, 1);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.load(ta, nukeValue(sab, 0));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 1);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.store
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+
+ Atomics.store(ta, 0, nukeValue(ta, 1));
+
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+
+ Atomics.store(ta, 0, nukeValue(sab, 1));
+
+ assertEq(ta[0], 1);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.compareExchange
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.compareExchange(ta, 0, 1, nukeValue(ta, 2));
+
+ assertEq(val, 1);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.compareExchange(ta, 0, 1, nukeValue(sab, 2));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 2);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.exchange
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.exchange(ta, 0, nukeValue(ta, 2));
+
+ assertEq(val, 1);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.exchange(ta, 0, nukeValue(sab, 2));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 2);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.add
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.add(ta, 0, nukeValue(ta, 2));
+
+ assertEq(val, 1);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 1;
+
+ let val = Atomics.add(ta, 0, nukeValue(sab, 2));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 3);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.sub
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 3;
+
+ let val = Atomics.sub(ta, 0, nukeValue(ta, 2));
+
+ assertEq(val, 3);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 3;
+
+ let val = Atomics.sub(ta, 0, nukeValue(sab, 2));
+
+ assertEq(val, 3);
+ assertEq(ta[0], 1);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.and
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 3;
+
+ let val = Atomics.and(ta, 0, nukeValue(ta, 1));
+
+ assertEq(val, 3);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 3;
+
+ let val = Atomics.and(ta, 0, nukeValue(sab, 1));
+
+ assertEq(val, 3);
+ assertEq(ta[0], 1);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.or
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 2;
+
+ let val = Atomics.or(ta, 0, nukeValue(ta, 1));
+
+ assertEq(val, 2);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 2;
+
+ let val = Atomics.or(ta, 0, nukeValue(sab, 1));
+
+ assertEq(val, 2);
+ assertEq(ta[0], 3);
+ assertIsDeadWrapper(sab);
+}
+
+// Atomics.xor
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 3;
+
+ let val = Atomics.xor(ta, 0, nukeValue(ta, 1));
+
+ assertEq(val, 3);
+ assertIsDeadWrapper(ta);
+}
+{
+ let sab = new otherGlobal.SharedArrayBuffer(4);
+ let ta = new otherGlobal.Int32Array(sab);
+ ta[0] = 3;
+
+ let val = Atomics.xor(ta, 0, nukeValue(sab, 1));
+
+ assertEq(val, 3);
+ assertEq(ta[0], 2);
+ assertIsDeadWrapper(sab);
+}
diff --git a/js/src/jit-test/tests/atomics/directives.txt b/js/src/jit-test/tests/atomics/directives.txt
new file mode 100644
index 0000000000..002776b8fe
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/directives.txt
@@ -0,0 +1 @@
+|jit-test| skip-if: !this.SharedArrayBuffer || !this.Atomics
diff --git a/js/src/jit-test/tests/atomics/inline-add.js b/js/src/jit-test/tests/atomics/inline-add.js
new file mode 100644
index 0000000000..bc1848cb22
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-add.js
@@ -0,0 +1,28 @@
+// |jit-test| slow;
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+// - the add operation is inlined as it should be
+// - loads and stores are not moved across the add
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function add(ta) {
+ var x = ta[0];
+ Atomics.add(ta, 86, 6);
+ var y = ta[1];
+ var z = y + 1;
+ var w = x + z;
+ return w;
+}
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new Int32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+ ia[i] = 37;
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+ v += add(ia);
+//print(v);
diff --git a/js/src/jit-test/tests/atomics/inline-add2.js b/js/src/jit-test/tests/atomics/inline-add2.js
new file mode 100644
index 0000000000..4a8447022e
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-add2.js
@@ -0,0 +1,28 @@
+// |jit-test| slow;
+//
+// Like inline-add, but with Uint32Array, which is a special case
+// because the value is representable only as a Number. All this
+// tests is that the Uint32 path is being triggered.
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+// - the add operation is inlined as it should be, with
+// a return type 'Double'
+// - loads and stores are not moved across the add
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function add(ta) {
+ return Atomics.add(ta, 86, 6);
+}
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new Uint32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+ ia[i] = 0xdeadbeef; // Important: Not an int32-capable value
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+ v += add(ia);
+//print(v);
diff --git a/js/src/jit-test/tests/atomics/inline-cmpxchg.js b/js/src/jit-test/tests/atomics/inline-cmpxchg.js
new file mode 100644
index 0000000000..a04e120f32
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-cmpxchg.js
@@ -0,0 +1,28 @@
+// |jit-test| slow;
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+// - the cmpxchg operation is inlined as it should be
+// - loads and stores are not moved across the cmpxchg
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function cmpxchg(ta) {
+ var x = ta[0];
+ Atomics.compareExchange(ta, 86, 37, 42);
+ var y = ta[1];
+ var z = y + 1;
+ var w = x + z;
+ return w;
+}
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new Int32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+ ia[i] = 37;
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+ v += cmpxchg(ia);
+//print(v);
diff --git a/js/src/jit-test/tests/atomics/memcpy-fidelity.js b/js/src/jit-test/tests/atomics/memcpy-fidelity.js
new file mode 100644
index 0000000000..81eb63fba2
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/memcpy-fidelity.js
@@ -0,0 +1,181 @@
+// In order not to run afoul of C++ UB we have our own non-C++ definitions of
+// operations (they are actually jitted) that can operate racily on shared
+// memory, see jit/shared/AtomicOperations-shared-jit.cpp.
+//
+// Operations on fixed-width 1, 2, 4, and 8 byte data are adequately tested
+// elsewhere. Here we specifically test our safe-when-racy replacements of
+// memcpy and memmove.
+//
+// There are two primitives in the engine, memcpy_down and memcpy_up. These are
+// equivalent except when data overlap, in which case memcpy_down handles
+// overlapping copies that move from higher to lower addresses and memcpy_up
+// handles ditto from lower to higher. memcpy uses memcpy_down always while
+// memmove selects the one to use dynamically based on its arguments.
+
+// Basic memcpy algorithm to be tested:
+//
+// - if src and target have the same alignment
+// - byte copy up to word alignment
+// - block copy as much as possible
+// - word copy as much as possible
+// - byte copy any tail
+// - else if on a platform that can deal with unaligned access
+// (ie, x86, ARM64, and ARM if the proper flag is set)
+// - block copy as much as possible
+// - word copy as much as possible
+// - byte copy any tail
+// - else // on a platform that can't deal with unaligned access
+// (ie ARM without the flag or x86 DEBUG builds with the
+// JS_NO_UNALIGNED_MEMCPY env var)
+// - block copy with byte copies
+// - word copy with byte copies
+// - byte copy any tail
+
+var target_buf = new SharedArrayBuffer(1024);
+var src_buf = new SharedArrayBuffer(1024);
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Different src and target buffer, this is memcpy "move down". The same
+// code is used in the engine for overlapping buffers when target addresses
+// are lower than source addresses.
+
+fill(src_buf);
+
+// Basic 1K perfectly aligned copy, copies blocks only.
+{
+ let target = new Uint8Array(target_buf);
+ let src = new Uint8Array(src_buf);
+ clear(target_buf);
+ target.set(src);
+ check(target_buf, 0, 1024, 0);
+}
+
+// Buffers are equally aligned but not on a word boundary and not ending on a
+// word boundary either, so this will copy first some bytes, then some blocks,
+// then some words, and then some bytes.
+{
+ let fill = 0x79;
+ clear(target_buf, fill);
+ let target = new Uint8Array(target_buf, 1, 1022);
+ let src = new Uint8Array(src_buf, 1, 1022);
+ target.set(src);
+ check_fill(target_buf, 0, 1, fill);
+ check(target_buf, 1, 1023, 1);
+ check_fill(target_buf, 1023, 1024, fill);
+}
+
+// Buffers are unequally aligned, we'll copy bytes only on some platforms and
+// unaligned blocks/words on others.
+{
+ clear(target_buf);
+ let target = new Uint8Array(target_buf, 0, 1023);
+ let src = new Uint8Array(src_buf, 1);
+ target.set(src);
+ check(target_buf, 0, 1023, 1);
+ check_zero(target_buf, 1023, 1024);
+}
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Overlapping src and target buffer and the target addresses are always
+// higher than the source addresses, this is memcpy "move up"
+
+// Buffers are equally aligned but not on a word boundary and not ending on a
+// word boundary either, so this will copy first some bytes, then some blocks,
+// then some words, and then some bytes.
+{
+ fill(target_buf);
+ let target = new Uint8Array(target_buf, 9, 999);
+ let src = new Uint8Array(target_buf, 1, 999);
+ target.set(src);
+ check(target_buf, 9, 1008, 1);
+ check(target_buf, 1008, 1024, 1008 & 255);
+}
+
+// Buffers are unequally aligned, we'll copy bytes only on some platforms and
+// unaligned blocks/words on others.
+{
+ fill(target_buf);
+ let target = new Uint8Array(target_buf, 2, 1022);
+ let src = new Uint8Array(target_buf, 1, 1022);
+ target.set(src);
+ check(target_buf, 2, 1024, 1);
+}
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Copy 0 to 127 bytes from and to a variety of addresses to check that we
+// handle limits properly in these edge cases.
+
+// Too slow in debug-noopt builds but we don't want to flag the test as slow,
+// since that means it'll never be run.
+
+if (this.getBuildConfiguration && !getBuildConfiguration().debug)
+{
+ let t = new Uint8Array(target_buf);
+ for (let my_src_buf of [src_buf, target_buf]) {
+ for (let size=0; size < 127; size++) {
+ for (let src_offs=0; src_offs < 8; src_offs++) {
+ for (let target_offs=0; target_offs < 8; target_offs++) {
+ clear(target_buf, Math.random()*255);
+ let target = new Uint8Array(target_buf, target_offs, size);
+
+ // Zero is boring
+ let bias = (Math.random() * 100 % 12) | 0;
+
+ // Note src may overlap target partially
+ let src = new Uint8Array(my_src_buf, src_offs, size);
+ for ( let i=0; i < size; i++ )
+ src[i] = i+bias;
+
+ // We expect these values to be unchanged by the copy
+ let below = target_offs > 0 ? t[target_offs - 1] : 0;
+ let above = t[target_offs + size];
+
+ // Copy
+ target.set(src);
+
+ // Verify
+ check(target_buf, target_offs, target_offs + size, bias);
+ if (target_offs > 0)
+ assertEq(t[target_offs-1], below);
+ assertEq(t[target_offs+size], above);
+ }
+ }
+ }
+ }
+}
+
+
+// Utilities
+
+function clear(buf, fill) {
+ let a = new Uint8Array(buf);
+ for ( let i=0; i < a.length; i++ )
+ a[i] = fill;
+}
+
+function fill(buf) {
+ let a = new Uint8Array(buf);
+ for ( let i=0; i < a.length; i++ )
+ a[i] = i & 255
+}
+
+function check(buf, from, to, startingWith) {
+ let a = new Uint8Array(buf);
+ for ( let i=from; i < to; i++ ) {
+ assertEq(a[i], startingWith);
+ startingWith = (startingWith + 1) & 255;
+ }
+}
+
+function check_zero(buf, from, to) {
+ check_fill(buf, from, to, 0);
+}
+
+function check_fill(buf, from, to, fill) {
+ let a = new Uint8Array(buf);
+ for ( let i=from; i < to; i++ )
+ assertEq(a[i], fill);
+}
diff --git a/js/src/jit-test/tests/atomics/mutual-exclusion.js b/js/src/jit-test/tests/atomics/mutual-exclusion.js
new file mode 100644
index 0000000000..2e1dd5485d
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/mutual-exclusion.js
@@ -0,0 +1,82 @@
+// |jit-test| skip-if: helperThreadCount() === 0 || getBuildConfiguration()["arm64-simulator"] === true
+
+// Let a few threads hammer on memory with atomics to provoke errors
+// in exclusion work. This test is not 100% fail-safe: the test may
+// pass despite a bug, but this is unlikely.
+
+// Map an Int32Array on shared memory. The first location is used as
+// a counter, each worker counts up on exit and the main thread will
+// wait until the counter reaches the number of workers. The other
+// elements are contended accumulators where we count up and down very
+// rapidly and for a long time, any failure in mutual exclusion should
+// lead to errors in the result. (For example, the test fails almost
+// immediately when I disable simulation of mutual exclusion in the
+// ARM simulator.)
+
+const numWorkers = 4; // You're not meant to change this
+const iterCount = 255; // Nor this
+const sabLength = 1024; // Nor this
+
+const oddResult = (function () {
+ var v = 0;
+ for ( var j=0 ; j < numWorkers ; j++ )
+ v |= (iterCount << (8 * j));
+ return v;
+})();
+
+const evenResult = 0;
+
+const sab = new SharedArrayBuffer(sabLength);
+
+setSharedObject(sab);
+
+const iab = new Int32Array(sab);
+
+function testRun(limit) {
+ console.log("Limit = " + limit);
+
+ // Fork off workers to hammer on memory.
+ for ( var i=0 ; i < numWorkers ; i++ ) {
+ evalInWorker(`
+ const iab = new Int32Array(getSharedObject());
+ const v = 1 << (8 * ${i});
+ for ( var i=0 ; i < ${limit} ; i++ ) {
+ for ( var k=0 ; k < ${iterCount} ; k++ ) {
+ if (i & 1) {
+ for ( var j=1 ; j < iab.length ; j++ )
+ Atomics.sub(iab, j, v);
+ }
+ else {
+ for ( var j=1 ; j < iab.length ; j++ )
+ Atomics.add(iab, j, v);
+ }
+ }
+ }
+ Atomics.add(iab, 0, 1);
+ `);
+ }
+
+ // Wait...
+ while (Atomics.load(iab, 0) != numWorkers)
+ ;
+ Atomics.store(iab, 0, 0);
+
+ // Check the results and clear the array again.
+ const v = (limit & 1) ? oddResult : evenResult;
+ for ( var i=1 ; i < iab.length ; i++ ) {
+ assertEq(iab[i], v);
+ iab[i] = 0;
+ }
+}
+
+// Under some configurations the test can take a while to run (and may
+// saturate the CPU since it runs four workers); try not to time out.
+
+var then = new Date();
+testRun(1);
+if (new Date() - then < 20000) {
+ testRun(2);
+ if (new Date() - then < 30000) {
+ testRun(3);
+ }
+}
diff --git a/js/src/jit-test/tests/atomics/nursery-non-shared-moved-gc.js b/js/src/jit-test/tests/atomics/nursery-non-shared-moved-gc.js
new file mode 100644
index 0000000000..472076ce63
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/nursery-non-shared-moved-gc.js
@@ -0,0 +1,99 @@
+// Small (non-shared) TypedArrays can use inline storage and can also be nursery
+// allocated. Ensure Atomics functions use correct rooting and don't store the
+// TypedArray's data pointer on the stack across a GC.
+const INLINE_MEMORY_LENGTH = 1;
+
+function gcValue(value) {
+ return {
+ valueOf() {
+ minorgc();
+ return value;
+ }
+ };
+};
+
+// Atomics.store
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+
+ Atomics.store(ta, 0, gcValue(1));
+
+ assertEq(ta[0], 1);
+}
+
+// Atomics.compareExchange
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 1;
+
+ let val = Atomics.compareExchange(ta, 0, 1, gcValue(2));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 2);
+}
+
+// Atomics.exchange
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 1;
+
+ let val = Atomics.exchange(ta, 0, gcValue(2));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 2);
+}
+
+// Atomics.add
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 1;
+
+ let val = Atomics.add(ta, 0, gcValue(2));
+
+ assertEq(val, 1);
+ assertEq(ta[0], 3);
+}
+
+// Atomics.sub
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 3;
+
+ let val = Atomics.sub(ta, 0, gcValue(2));
+
+ assertEq(val, 3);
+ assertEq(ta[0], 1);
+}
+
+// Atomics.and
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 3;
+
+ let val = Atomics.and(ta, 0, gcValue(1));
+
+ assertEq(val, 3);
+ assertEq(ta[0], 1);
+}
+
+// Atomics.or
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 2;
+
+ let val = Atomics.or(ta, 0, gcValue(1));
+
+ assertEq(val, 2);
+ assertEq(ta[0], 3);
+}
+
+// Atomics.xor
+{
+ let ta = new Int32Array(INLINE_MEMORY_LENGTH);
+ ta[0] = 3;
+
+ let val = Atomics.xor(ta, 0, gcValue(1));
+
+ assertEq(val, 3);
+ assertEq(ta[0], 2);
+}
diff --git a/js/src/jit-test/tests/atomics/optimization-tests.js b/js/src/jit-test/tests/atomics/optimization-tests.js
new file mode 100644
index 0000000000..65b2055dc6
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/optimization-tests.js
@@ -0,0 +1,103 @@
+// Some optimization tests for the Atomics primitives.
+//
+// These do not test atomicity, just code generation on a single
+// thread.
+//
+// It's useful to look at the code generated for this test with -D to
+// the JS shell.
+//
+// Bug 1138348 - unconstrained use of byte registers on x64
+// Bug 1077014 - code generation for Atomic operations for effect,
+// all platforms
+// Bug 1141121 - immediate operand in atomic operations on x86/x64
+
+function test(SharedOrUnsharedArrayBuffer) {
+var sum = 0;
+
+function f(ia, k) {
+ // For effect, variable value. The generated code on x86/x64
+ // should be one LOCK ADDB. (On ARM, there should be no
+ // sign-extend of the current value in the cell, otherwise this is
+ // still a LDREX/STREX loop.)
+ Atomics.add(ia, 0, k);
+
+ // Ditto constant value. Here the LOCK ADDB should have an
+ // immediate operand.
+ Atomics.add(ia, 0, 1);
+}
+
+function f2(ia, k) {
+ // For effect, variable value and constant value. The generated
+ // code on x86/x64 should be one LOCK SUBB.
+ Atomics.sub(ia, 2, k);
+
+ // Ditto constant value. Here the LOCK SUBB should have an
+ // immediate operand.
+ Atomics.sub(ia, 2, 1);
+}
+
+function f4(ia, k) {
+ // For effect, variable value. The generated code on x86/x64
+ // should be one LOCK ORB. (On ARM, there should be no
+ // sign-extend of the current value in the cell, otherwise this is
+ // still a LDREX/STREX loop.)
+ Atomics.or(ia, 6, k);
+
+ // Ditto constant value. Here the LOCK ORB should have an
+ // immediate operand.
+ Atomics.or(ia, 6, 1);
+}
+
+function g(ia, k) {
+ // For its value, variable value. The generated code on x86/x64
+ // should be one LOCK XADDB.
+ sum += Atomics.add(ia, 1, k);
+
+ // Ditto constant value. XADD does not admit an immediate
+ // operand, so in the second case there should be a preliminary
+ // MOV of the immediate to the output register.
+ sum += Atomics.add(ia, 1, 1);
+}
+
+function g2(ia, k) {
+ // For its value, variable value. The generated code on x86/x64
+ // should be one LOCK XADDB, preceded by a NEG into the output
+ // register instead of a MOV.
+ sum += Atomics.sub(ia, 3, k);
+
+ // Ditto constant value. XADD does not admit an immediate
+ // operand, so in the second case there should be a preliminary
+ // MOV of the negated immediate to the output register.
+ sum += Atomics.sub(ia, 3, 1);
+}
+
+function g4(ia, k) {
+ // For its value, variable value. The generated code on x86/x64
+ // should be a loop around ORB ; CMPXCHGB
+ sum += Atomics.or(ia, 7, k);
+
+ // Ditto constant value. Here the ORB in the loop should have
+ // an immediate operand.
+ sum += Atomics.or(ia, 7, 1);
+}
+
+var i8a = new Int8Array(new SharedOrUnsharedArrayBuffer(65536));
+for ( var i=0 ; i < 10000 ; i++ ) {
+ f(i8a, i % 10);
+ g(i8a, i % 10);
+ f2(i8a, i % 10);
+ g2(i8a, i % 10);
+ f4(i8a, i % 10);
+ g4(i8a, i % 10);
+}
+
+assertEq(i8a[0], ((10000 + 10000*4.5) << 24) >> 24);
+assertEq(i8a[1], ((10000 + 10000*4.5) << 24) >> 24);
+assertEq(i8a[2], ((-10000 + -10000*4.5) << 24) >> 24);
+assertEq(i8a[3], ((-10000 + -10000*4.5) << 24) >> 24);
+assertEq(i8a[6], 15);
+assertEq(i8a[7], 15);
+}
+
+test(SharedArrayBuffer);
+test(ArrayBuffer); \ No newline at end of file
diff --git a/js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js b/js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js
new file mode 100644
index 0000000000..f689511ba4
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js
@@ -0,0 +1,44 @@
+function test(SharedOrUnsharedArrayBuffer) {
+var ia = new Int32Array(new SharedOrUnsharedArrayBuffer(4));
+
+// Atomics.store() returns the input value converted to integer as if
+// by ToInteger.
+//
+// JIT and interpreter have different paths here, so loop a little to
+// trigger the JIT properly.
+
+function f() {
+ assertEq(Atomics.store(ia, 0, 3.5), 3);
+ assertEq(ia[0], 3);
+
+ assertEq(Atomics.store(ia, 0, -0), +0);
+ assertEq(ia[0], 0);
+
+ assertEq(Atomics.store(ia, 0, '4.6'), 4);
+ assertEq(ia[0], 4);
+
+ assertEq(Atomics.store(ia, 0, '-4.6'), -4);
+ assertEq(ia[0], -4);
+
+ assertEq(Atomics.store(ia, 0, undefined), 0);
+ assertEq(ia[0], 0);
+
+ assertEq(Atomics.store(ia, 0, Infinity), Infinity);
+ assertEq(ia[0], 0);
+
+ assertEq(Atomics.store(ia, 0, -Infinity), -Infinity);
+ assertEq(ia[0], 0);
+
+ assertEq(Atomics.store(ia, 0, Math.pow(2, 32)+5), Math.pow(2, 32)+5);
+ assertEq(ia[0], 5);
+
+ assertEq(Atomics.store(ia, 0, { valueOf: () => 3.7 }), 3);
+ assertEq(ia[0], 3);
+}
+
+for ( var i=0 ; i < 10 ; i++ )
+ f();
+}
+
+test(SharedArrayBuffer);
+test(ArrayBuffer);