diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit-test/tests/atomics | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/atomics')
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); |