From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- js/src/jit-test/tests/atomics/basic-tests.js | 564 +++++++++++++++++++++++++++ 1 file changed, 564 insertions(+) create mode 100644 js/src/jit-test/tests/atomics/basic-tests.js (limited to 'js/src/jit-test/tests/atomics/basic-tests.js') 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); -- cgit v1.2.3