diff options
Diffstat (limited to '')
228 files changed, 12827 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/cacheir/1877684.js b/js/src/jit-test/tests/cacheir/1877684.js new file mode 100644 index 0000000000..a926db32fe --- /dev/null +++ b/js/src/jit-test/tests/cacheir/1877684.js @@ -0,0 +1,6 @@ +a = 0 +b = [ + a, + new Float64Array +] +for (c = 0; c < 10000; ++c) b[c & 1][0] = true diff --git a/js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js b/js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js new file mode 100644 index 0000000000..2958ce2283 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js @@ -0,0 +1,160 @@ +// Add dense elements to packed and non-packed arrays. Cover both mono- and +// polymorphic call sites. Change array to non-extensible during execution. + +function testAddDenseEmpty() { + var array = []; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + store(array, i); + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseEmpty(); + +function testAddDensePacked() { + var array = [0, 1]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + store(array, i); + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDensePacked(); + +function testAddDenseNonPacked() { + var array = [/* hole */, 1]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + store(array, i); + } + + assertEq(array.length, 5); + assertEq(0 in array, false); + for (var i = 1; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseNonPacked(); + +function testAddDenseEmptyPoly() { + var array = []; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseEmptyPoly(); + +function testAddDensePackedPoly() { + var array = [0, 1]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDensePackedPoly(); + +function testAddDenseNonPackedPoly() { + var array = [/* hole */, 1]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + assertEq(0 in array, false); + for (var i = 1; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseNonPackedPoly(); diff --git a/js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js b/js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js new file mode 100644 index 0000000000..635ee07186 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js @@ -0,0 +1,160 @@ +// Add dense elements to packed and non-packed arrays. Cover both mono- and +// polymorphic call sites. Change array length to non-writable during execution. + +function testAddDenseEmpty() { + var array = []; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + store(array, i); + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseEmpty(); + +function testAddDensePacked() { + var array = [0, 1]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + store(array, i); + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDensePacked(); + +function testAddDenseNonPacked() { + var array = [/* hole */, 1]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + store(array, i); + } + + assertEq(array.length, 5); + assertEq(0 in array, false); + for (var i = 1; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseNonPacked(); + +function testAddDenseEmptyPoly() { + var array = []; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseEmptyPoly(); + +function testAddDensePackedPoly() { + var array = [0, 1]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDensePackedPoly(); + +function testAddDenseNonPackedPoly() { + var array = [/* hole */, 1]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 2; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + assertEq(0 in array, false); + for (var i = 1; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testAddDenseNonPackedPoly(); diff --git a/js/src/jit-test/tests/cacheir/add-dense-element.js b/js/src/jit-test/tests/cacheir/add-dense-element.js new file mode 100644 index 0000000000..099e941648 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/add-dense-element.js @@ -0,0 +1,124 @@ +// Add dense elements to packed and non-packed arrays. Cover both mono- and +// polymorphic call sites. + +function testAddDenseEmpty() { + var array = []; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + store(array, i); + } + + assertEq(array.length, 10); + for (var i = 0; i < 10; ++i) { + assertEq(array[i], i); + } +} +testAddDenseEmpty(); + +function testAddDensePacked() { + var array = [0, 1]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 2; i < 10; ++i) { + store(array, i); + } + + assertEq(array.length, 10); + for (var i = 0; i < 10; ++i) { + assertEq(array[i], i); + } +} +testAddDensePacked(); + +function testAddDenseNonPacked() { + var array = [/* hole */, 1]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 2; i < 10; ++i) { + store(array, i); + } + + assertEq(array.length, 10); + assertEq(0 in array, false); + for (var i = 1; i < 10; ++i) { + assertEq(array[i], i); + } +} +testAddDenseNonPacked(); + +function testAddDenseEmptyPoly() { + var array = []; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 10); + for (var i = 0; i < 10; ++i) { + assertEq(array[i], i); + } +} +testAddDenseEmptyPoly(); + +function testAddDensePackedPoly() { + var array = [0, 1]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 2; i < 10; ++i) { + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 10); + for (var i = 0; i < 10; ++i) { + assertEq(array[i], i); + } +} +testAddDensePackedPoly(); + +function testAddDenseNonPackedPoly() { + var array = [/* hole */, 1]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 2; i < 10; ++i) { + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 10); + assertEq(0 in array, false); + for (var i = 1; i < 10; ++i) { + assertEq(array[i], i); + } +} +testAddDenseNonPackedPoly(); diff --git a/js/src/jit-test/tests/cacheir/add-function-prototype.js b/js/src/jit-test/tests/cacheir/add-function-prototype.js new file mode 100644 index 0000000000..e5a77d75df --- /dev/null +++ b/js/src/jit-test/tests/cacheir/add-function-prototype.js @@ -0,0 +1,55 @@ +function checkPrototype(fun, proto, resolvesPrototype) { + var desc = Object.getOwnPropertyDescriptor(fun, "prototype"); + assertEq(desc.value, proto); + assertEq(desc.configurable, !resolvesPrototype); + assertEq(desc.enumerable, !resolvesPrototype); + assertEq(desc.writable, true); +} +function addPrototype(fun, proto, resolvesPrototype) { + fun.prototype = proto; + checkPrototype(fun, proto, resolvesPrototype); +} +function test() { + for (var i=0; i<50; i++) { + addPrototype(function() {}, i, true); + addPrototype(function*() {}, i, true); + addPrototype(function async() {}, i, true); + // Builtins, arrow functions, bound functions don't have a default + // prototype property. + addPrototype(Math.abs, i, false); + addPrototype(Array.prototype.map, i, false); + addPrototype(() => 1, i, false); + addPrototype((function() {}).bind(null), i, false); + } + + // Now test this with a different IC for each function type. + for (var i=0; i<50; i++) { + var f = function() {}; + f.prototype = i; + checkPrototype(f, i, true); + + f = function*() {}; + f.prototype = i; + checkPrototype(f, i, true); + + f = function async() {}; + f.prototype = i; + checkPrototype(f, i, true); + + Math.sin.prototype = i; + checkPrototype(Math.sin, i, false); + + Array.prototype.filter.prototype = i; + checkPrototype(Array.prototype.filter, i, false); + + f = () => 1; + f.prototype = i; + checkPrototype(f, i, false); + + f = (function() {}).bind(null); + f.prototype = i; + checkPrototype(f, i, false); + } + +} +test(); diff --git a/js/src/jit-test/tests/cacheir/alloc-dense-elements.js b/js/src/jit-test/tests/cacheir/alloc-dense-elements.js new file mode 100644 index 0000000000..3013aa9ec1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/alloc-dense-elements.js @@ -0,0 +1,9 @@ +function f() { + for (var i=0; i<100; i++) { + // Int32Array to force an IC in Ion. + var o = (i == 20) ? new Int32Array(1) : {}; + o[0] = i; + assertEq(o[0], i); + } +} +f(); diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-1.js b/js/src/jit-test/tests/cacheir/apply-minmax-1.js new file mode 100644 index 0000000000..3fbf48aba0 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/apply-minmax-1.js @@ -0,0 +1,23 @@ +function testMin(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.min.apply(null, arr); + } + return sum; +} + +function testMax(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.max.apply(null, arr); + } + return sum; +} + +// Attach Int32MinMaxArrayResult. +assertEq(testMin([1,2,3,4,5]), 50); +assertEq(testMax([1,2,3,4,5]), 250); + +// Verify that we handle an empty list correctly. +assertEq(testMin([]), Infinity); +assertEq(testMax([]), -Infinity); diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-2.js b/js/src/jit-test/tests/cacheir/apply-minmax-2.js new file mode 100644 index 0000000000..28ff687329 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/apply-minmax-2.js @@ -0,0 +1,23 @@ +function testMin(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.min.apply(null, arr); + } + return sum; +} + +function testMax(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.max.apply(null, arr); + } + return sum; +} + +// Attach Int32MinMaxArrayResult. +assertEq(testMin([1,2,3,4,5]), 50); +assertEq(testMax([1,2,3,4,5]), 250); + +// Verify that we handle a double element correctly. +assertEq(testMin([1,2,3.5,4,5]), 50); +assertEq(testMax([1,2,3.5,4,5]), 250); diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-3.js b/js/src/jit-test/tests/cacheir/apply-minmax-3.js new file mode 100644 index 0000000000..3cfad3f958 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/apply-minmax-3.js @@ -0,0 +1,25 @@ +function testMin(arr) { + return Math.min.apply(Math, arr); +} + +function testMax(arr) { + return Math.max.apply(Math, arr); +} + +with({}) {} + +// Warp-compile. +var sum = 0; +for (var i = 0; i < 50; i++) { + sum += testMin([1, 2.5, 3]); + sum += testMax([1, 2.5, 3]); +} +assertEq(sum, 200); + +// Test min/max with no arguments. +assertEq(testMin([]), Infinity); +assertEq(testMax([]), -Infinity); + +// Test NaN. +assertEq(testMin([1,NaN]), NaN); +assertEq(testMax([1,NaN]), NaN); diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-4.js b/js/src/jit-test/tests/cacheir/apply-minmax-4.js new file mode 100644 index 0000000000..fe001668f9 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/apply-minmax-4.js @@ -0,0 +1,15 @@ +function f(...rest) { + for (var i = 0; i < 100; ++i) { + rest[0] = 0; + var v = Math.max.apply(Math, rest); + + rest[0] = i; + var w = Math.max.apply(Math, rest); + + assertEq(v, 0); + assertEq(w, i); + } +} +for (var i = 0; i < 2; ++i) { + f(0, 0); +} diff --git a/js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js b/js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js new file mode 100644 index 0000000000..0b6024d9af --- /dev/null +++ b/js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js @@ -0,0 +1,171 @@ +// Test iteration with a mapped arguments object. + +function simple() { + function f() { + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i < 100; ++i) { + assertEq(f(1, 2, 3), 6); + } +} +simple(); + +function spreadCall() { + function f() { + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + function g() { + return f(...arguments); + } + + for (var i = 0; i < 100; ++i) { + assertEq(g(1, 2, 3), 6); + } +} +spreadCall(); + +function spreadArray() { + function f() { + var arr = [...arguments]; + var sum = 0; + for (var v of arr) { + sum += v; + } + return sum; + } + + for (var i = 0; i < 100; ++i) { + assertEq(f(1, 2, 3), 6); + } +} +spreadArray(); + +function reifyIterator() { + var reify = false; + function f() { + if (reify) { + // Redefining any property attributes will reify the iterator property. + Object.defineProperty(arguments, Symbol.iterator, { + writable: false + }); + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i <= 100; ++i) { + reify = i >= 50; + assertEq(f(1, 2, 3), 6); + } +} +reifyIterator(); + +function overwriteIterator() { + var callCount = 0; + function Iterator() { + callCount += 1; + return Array.prototype[Symbol.iterator].call(this); + } + + var overwrite = false; + function f() { + if (overwrite) { + arguments[Symbol.iterator] = Iterator; + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i <= 100; ++i) { + overwrite = i > 50; + assertEq(f(1, 2, 3), 6); + } + assertEq(callCount, 50); +} +overwriteIterator(); + +function deleteIterator() { + var remove = false; + function f() { + // Deleting Symbol.iterator won't change the shape of the arguments object. + // That's why we need to use a separate guard instruction to check if the + // iterator property was modified. + if (remove) { + delete arguments[Symbol.iterator]; + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + var error; + try { + for (var i = 0; i <= 100; ++i) { + remove = i === 100; + assertEq(f(1, 2, 3), 6); + } + } catch (e) { + error = e; + } + assertEq(error instanceof TypeError, true); +} +deleteIterator(); + +function deleteIteratorInherit() { + var callCount = 0; + function Iterator() { + callCount += 1; + return Array.prototype[Symbol.iterator].call(this); + } + + Object.prototype[Symbol.iterator] = Iterator; + + var remove = false; + function f() { + // Deleting Symbol.iterator won't change the shape of the arguments object. + // That's why we need to use a separate guard instruction to check if the + // iterator property was modified. + if (remove) { + delete arguments[Symbol.iterator]; + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i <= 100; ++i) { + remove = i === 100; + assertEq(f(1, 2, 3), 6); + } + assertEq(callCount, 1); + + delete Object.prototype[Symbol.iterator]; +} +deleteIteratorInherit(); + +// Don't add tests below this point because |Object.prototype[Symbol.iterator]| +// was modified, which may lead to engine-wide deoptimisations. diff --git a/js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js b/js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js new file mode 100644 index 0000000000..29b2163596 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js @@ -0,0 +1,184 @@ +// Test iteration with an unmapped arguments object. + +function simple() { + function f() { + "use strict"; + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i < 100; ++i) { + assertEq(f(1, 2, 3), 6); + } +} +simple(); + +function spreadCall() { + function f() { + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + function g() { + "use strict"; + return f(...arguments); + } + + for (var i = 0; i < 100; ++i) { + assertEq(g(1, 2, 3), 6); + } +} +spreadCall(); + +function spreadArray() { + function f() { + "use strict"; + + var arr = [...arguments]; + var sum = 0; + for (var v of arr) { + sum += v; + } + return sum; + } + + for (var i = 0; i < 100; ++i) { + assertEq(f(1, 2, 3), 6); + } +} +spreadArray(); + +function reifyIterator() { + var reify = false; + function f() { + "use strict"; + + if (reify) { + // Redefining any property attributes will reify the iterator property. + Object.defineProperty(arguments, Symbol.iterator, { + writable: false + }); + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i <= 100; ++i) { + reify = i >= 50; + assertEq(f(1, 2, 3), 6); + } +} +reifyIterator(); + +function overwriteIterator() { + var callCount = 0; + function Iterator() { + callCount += 1; + return Array.prototype[Symbol.iterator].call(this); + } + + var overwrite = false; + function f() { + "use strict"; + + if (overwrite) { + arguments[Symbol.iterator] = Iterator; + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i <= 100; ++i) { + overwrite = i > 50; + assertEq(f(1, 2, 3), 6); + } + assertEq(callCount, 50); +} +overwriteIterator(); + +function deleteIterator() { + var remove = false; + function f() { + "use strict"; + + // Deleting Symbol.iterator won't change the shape of the arguments object. + // That's why we need to use a separate guard instruction to check if the + // iterator property was modified. + if (remove) { + delete arguments[Symbol.iterator]; + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + var error; + try { + for (var i = 0; i <= 100; ++i) { + remove = i === 100; + assertEq(f(1, 2, 3), 6); + } + } catch (e) { + error = e; + } + assertEq(error instanceof TypeError, true); +} +deleteIterator(); + +function deleteIteratorInherit() { + var callCount = 0; + function Iterator() { + callCount += 1; + return Array.prototype[Symbol.iterator].call(this); + } + + Object.prototype[Symbol.iterator] = Iterator; + + var remove = false; + function f() { + "use strict"; + + // Deleting Symbol.iterator won't change the shape of the arguments object. + // That's why we need to use a separate guard instruction to check if the + // iterator property was modified. + if (remove) { + delete arguments[Symbol.iterator]; + } + + var sum = 0; + for (var v of arguments) { + sum += v; + } + return sum; + } + + for (var i = 0; i <= 100; ++i) { + remove = i === 100; + assertEq(f(1, 2, 3), 6); + } + assertEq(callCount, 1); + + delete Object.prototype[Symbol.iterator]; +} +deleteIteratorInherit(); + +// Don't add tests below this point because |Object.prototype[Symbol.iterator]| +// was modified, which may lead to engine-wide deoptimisations. diff --git a/js/src/jit-test/tests/cacheir/array-slice.js b/js/src/jit-test/tests/cacheir/array-slice.js new file mode 100644 index 0000000000..2ed43926ff --- /dev/null +++ b/js/src/jit-test/tests/cacheir/array-slice.js @@ -0,0 +1,39 @@ +function packed() { + var a = [0, 1, 2, 3]; + for (var i = 0; i <= 100; ++i) { + var r = a.slice(0); + assertEq(r.length, 4); + } +} + +for (var i = 0; i < 2; ++i) { + packed(); +} + +function packedThenUnpacked() { + var a = [0, 1, 2, 3]; + var q = 0; + for (var i = 0; i <= 100; ++i) { + if (i === 100) a[10] = 0; + + var r = a.slice(0); + assertEq(r.length, i < 100 ? 4 : 11); + } +} + +for (var i = 0; i < 2; ++i) { + packedThenUnpacked(); +} + +function unpacked() { + var a = [0, 1, /* hole */ , 3]; + for (var i = 0; i <= 100; ++i) { + var r = a.slice(0); + assertEq(r.length, 4); + assertEq(2 in r, false); + } +} + +for (var i = 0; i < 2; ++i) { + unpacked(); +} diff --git a/js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js b/js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js new file mode 100644 index 0000000000..3165bd7940 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js @@ -0,0 +1,123 @@ +const types = [ + "Int8", + "Int16", + "Int32", + "Uint8", + "Uint16", + "Uint32", +]; + +function convert(type, value) { + let num = Number(value); + switch (type) { + case "Int8": + return ((num | 0) << 24) >> 24; + case "Int16": + return ((num | 0) << 16) >> 16; + case "Int32": + return (num | 0); + case "Uint8": + return (num >>> 0) & 0xff; + case "Uint16": + return (num >>> 0) & 0xffff; + case "Uint32": + return (num >>> 0); + case "Uint8Clamped": { + if (Number.isNaN(num)) { + return 0; + } + let clamped = Math.max(0, Math.min(num, 255)); + let f = Math.floor(clamped); + if (clamped < f + 0.5) { + return f; + } + if (clamped > f + 0.5) { + return f + 1; + } + return f + (f & 1); + } + case "Float32": + return Math.fround(num); + case "Float64": + return num; + } + throw new Error(); +} + + +function runTest(type, initial, values) { + let expected = values.map(v => convert(type, v)); + assertEq( + expected.some(e => Object.is(e, initial)), + false, + "initial must be different from the expected values" + ); + + // Create a fresh function to ensure ICs are specific to a single TypedArray kind. + let test = Function("initial, values, expected", ` + let ta = new ${type}Array(1); + for (let i = 0; i < 200; ++i) { + ta[0] = initial; + Atomics.store(ta, 0, values[i % values.length]); + assertEq(ta[0], expected[i % expected.length]); + } + `); + test(initial, values, expected); +} + +const tests = [ + // |null| is coerced to zero. + { + initial: 1, + values: [null], + }, + + // |undefined| is coerced to zero or NaN. + { + initial: 1, + values: [undefined], + }, + + // |false| is coerced to zero and |true| is coerced to one. + { + initial: 2, + values: [false, true], + }, + + // Strings without a fractional part. + { + initial: 42, + values: [ + "0", "1", "10", "111", "128", "256", "0x7fffffff", "0xffffffff", + ], + }, + + // Strings without a fractional part, but a leading minus sign. + { + initial: 42, + values: [ + "-0", "-1", "-10", "-111", "-128", "-256", "-2147483647", "-4294967295", + ], + }, + + // Strings with a fractional number part. + { + initial: 42, + values: [ + "0.1", "1.2", "10.8", "111.9", + "-0.1", "-1.2", "-10.8", "-111.9", + ], + }, + + // Special values and strings not parseable as a number. + { + initial: 42, + values: ["Infinity", "-Infinity", "NaN", "foobar"], + }, +]; + +for (let type of types) { + for (let {initial, values} of tests) { + runTest(type, initial, values); + } +} diff --git a/js/src/jit-test/tests/cacheir/bigint-binary.js b/js/src/jit-test/tests/cacheir/bigint-binary.js new file mode 100644 index 0000000000..773178cc3c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-binary.js @@ -0,0 +1,190 @@ +var xs = [ + // Definitely heap digits. + -(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, +]; + +function testAdd() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = xs[xs.length - 1 - j]; + + assertEq(x + 0n, x); + assertEq(x + y, 0n); + } +} +testAdd(); + +function testSub() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = xs[xs.length - 1 - j]; + + assertEq(x - 0n, x); + assertEq(x - (-y), 0n); + } +} +testSub(); + +function testMul() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x * 0n, 0n); + assertEq(x * 1n, x); + assertEq(x * (-1n), -x); + } +} +testMul(); + +function testDiv() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + + // Don't divide by zero. + if (j === xs.length >> 1) { + assertEq(x / 1n, 0n); + continue; + } + + assertEq(x / x, 1n); + assertEq(x / 1n, x); + } +} +testDiv(); + +function testMod() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + + // Don't divide by zero. + if (j === xs.length >> 1) { + assertEq(x / 1n, 0n); + continue; + } + + assertEq(x % x, 0n); + assertEq(x % 1n, 0n); + } +} +testMod(); + +function testPow() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x ** 0n, 1n); + assertEq(x ** 1n, x); + } +} +testPow(); + +function testBitAnd() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x & x, x); + assertEq(x & 0n, 0n); + } +} +testBitAnd(); + +function testBitOr() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x | x, x); + assertEq(x | 0n, x); + } +} +testBitOr(); + +function testBitXor() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x ^ x, 0n); + assertEq(x ^ 0n, x); + } +} +testBitXor(); + +function testLeftShift() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x << 0n, x); + assertEq(x << 1n, x * 2n); + if (x >= 0n || !(x & 1n)) { + assertEq(x << -1n, x / 2n); + } else { + assertEq(x << -1n, (x / 2n) - 1n); + } + } +} +testLeftShift(); + +function testRightShift() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + assertEq(x >> 0n, x); + if (x >= 0n || !(x & 1n)) { + assertEq(x >> 1n, x / 2n); + } else { + assertEq(x >> 1n, (x / 2n) - 1n); + } + assertEq(x >> -1n, x * 2n); + } +} +testRightShift(); diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-double.js b/js/src/jit-test/tests/cacheir/bigint-compare-double.js new file mode 100644 index 0000000000..ea61e4c558 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-compare-double.js @@ -0,0 +1,209 @@ +// Same test as bigint-compare-number, except that the inputs are always Double typed. + +// Test various combinations of (BigInt R Double) and ensures the result is consistent with +// (Double R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R Double), +// but merely ensures the possible combinations are properly handled in CacheIR. + +var xs = [ + // 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 Double(x) { + // numberToDouble always returns a Double valued number. + return numberToDouble(x); +} + +// Compute the Double approximation of the BigInt values. +var ys = xs.map(x => Double(Number(x))); + +// Compute if the Double approximation of the BigInt values is exact. +// (The larger test values are all powers of two, so we can keep this function simple.) +var zs = xs.map(x => { + var isNegative = x < 0n; + if (isNegative) { + x = -x; + } + var s = x.toString(2); + if (s.length <= 53 || (s.length <= 1024 && /^1+0+$/.test(s))) { + return 0; + } + if (s.length <= 1024 && /^1+$/.test(s)) { + return isNegative ? -1 : 1; + } + if (s.length <= 1024 && /^1+0+1$/.test(s)) { + return isNegative ? 1 : -1; + } + return NaN; +}); + +function testLooseEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x == y, z === 0); + assertEq(y == x, z === 0); + } +} +testLooseEqual(); + +function testLooseNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x != y, z !== 0); + assertEq(y != x, z !== 0); + } +} +testLooseNotEqual(); + +function testLessThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x < y, false); + assertEq(y < x, false); + } else if (z > 0) { + assertEq(x < y, true); + assertEq(y < x, false); + } else if (z < 0) { + assertEq(x < y, false); + assertEq(y < x, true); + } else { + assertEq(x < y, y > 0); + assertEq(y < x, y < 0); + } + } +} +testLessThan(); + +function testLessThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x <= y, true); + assertEq(y <= x, true); + } else if (z > 0) { + assertEq(x <= y, true); + assertEq(y <= x, false); + } else if (z < 0) { + assertEq(x <= y, false); + assertEq(y <= x, true); + } else { + assertEq(x <= y, y > 0); + assertEq(y <= x, y < 0); + } + } +} +testLessThanEquals(); + +function testGreaterThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x > y, false); + assertEq(y > x, false); + } else if (z > 0) { + assertEq(x > y, false); + assertEq(y > x, true); + } else if (z < 0) { + assertEq(x > y, true); + assertEq(y > x, false); + } else { + assertEq(x > y, y < 0); + assertEq(y > x, y > 0); + } + } +} +testGreaterThan(); + +function testGreaterThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x >= y, true); + assertEq(y >= x, true); + } else if (z > 0) { + assertEq(x >= y, false); + assertEq(y >= x, true); + } else if (z < 0) { + assertEq(x >= y, true); + assertEq(y >= x, false); + } else { + assertEq(x >= y, y < 0); + assertEq(y >= x, y > 0); + } + } +} +testGreaterThanEquals(); diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-int32.js b/js/src/jit-test/tests/cacheir/bigint-compare-int32.js new file mode 100644 index 0000000000..16f6a31ad0 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-compare-int32.js @@ -0,0 +1,258 @@ +// Extensive test for (BigInt R Int32) comparison operations, testing the output +// is correct and consistent with (Int32 R⁻¹ BigInt). + +function gcd(a, b) { + a |= 0; + b |= 0; + while (b !== 0) { + [a, b] = [b, a % b]; + } + return Math.abs(a); +} + +const ITERATIONS = 150; + +function assertAllCombinationsTested(xs, ys, n) { + // If the array lengths are relatively prime and their product is at least + // |n| long, all possible combinations are tested at least once. Make sure + // we test each combination at least three times. + var m = 3; + + assertEq(gcd(xs.length, ys.length), 1); + assertEq(m * xs.length * ys.length <= n, true); +} + +function LessThan(xs, ys, n = ITERATIONS) { + assertAllCombinationsTested(xs, ys, n); + for (var i = 0; i < n; ++i) { + var x = xs[i % xs.length]; + var y = ys[i % ys.length]|0; // Ensure int32 typed + + assertEq(x == y, false); + assertEq(y == x, false); + + assertEq(x != y, true); + assertEq(y != x, true); + + assertEq(x < y, true); + assertEq(y < x, false); + + assertEq(x <= y, true); + assertEq(y <= x, false); + + assertEq(x > y, false); + assertEq(y > x, true); + + assertEq(x >= y, false); + assertEq(y >= x, true); + } +} + +function GreaterThan(xs, ys, n = ITERATIONS) { + assertAllCombinationsTested(xs, ys, n); + for (var i = 0; i < n; ++i) { + var x = xs[i % xs.length]; + var y = ys[i % ys.length]|0; // Ensure int32 typed + + assertEq(x == y, false); + assertEq(y == x, false); + + assertEq(x != y, true); + assertEq(y != x, true); + + assertEq(x < y, false); + assertEq(y < x, true); + + assertEq(x <= y, false); + assertEq(y <= x, true); + + assertEq(x > y, true); + assertEq(y > x, false); + + assertEq(x >= y, true); + assertEq(y >= x, false); + } +} + +function Equal(xs, ys, n = ITERATIONS) { + assertAllCombinationsTested(xs, ys, n); + for (var i = 0; i < n; ++i) { + var x = xs[i % xs.length]; + var y = ys[i % ys.length]|0; // Ensure int32 typed + + assertEq(x == y, true); + assertEq(y == x, true); + + assertEq(x != y, false); + assertEq(y != x, false); + + assertEq(x < y, false); + assertEq(y < x, false); + + assertEq(x <= y, true); + assertEq(y <= x, true); + + assertEq(x > y, false); + assertEq(y > x, false); + + assertEq(x >= y, true); + assertEq(y >= x, true); + } +} + +function test(fn) { + // Clone the test function to ensure a new function is compiled each time. + return Function(`return ${fn}`)(); +} + +const negativeInt32 = [-2147483648, -2147483647, -1]; +const zeroInt32 = [0]; +const positiveInt32 = [1, 2147483646, 2147483647]; +const zeroOrPositiveInt32 = [...zeroInt32, ...positiveInt32]; +const anyInt32 = [...negativeInt32, ...zeroInt32, ...positiveInt32]; + +// Test when the BigInt is too large to be representable as a single BigInt digit. +function testLarge() { + var xs = [ + 2n ** 32n, // exceeds single digit limit on 32-bit + 2n ** 64n, // exceeds single digit limit on 64-bit + 2n ** 96n, // not a single digit on either platform + ]; + test(GreaterThan)(xs, anyInt32); + + var xs = [ + -(2n ** 32n), // exceeds single digit limit on 32-bit + -(2n ** 64n), // exceeds single digit limit on 64-bit + -(2n ** 96n), // not a single digit on either platform + ]; + test(LessThan)(xs, anyInt32); +} +testLarge(); + +// Test when the BigInt is 0n. +function testZero() { + var xs = [ + 0n + ]; + + test(GreaterThan)(xs, negativeInt32); + test(Equal)(xs, zeroInt32); + test(LessThan)(xs, positiveInt32); +} +testZero(); + +// Test when both numbers are negative. +function testNegative() { + var xs = [ + -(2n ** 64n) - 2n, + -(2n ** 64n) - 1n, // Max negative using a single BigInt digit on 64-bit. + -(2n ** 64n), + + -(2n ** 32n) - 2n, + -(2n ** 32n) - 1n, // Max negative using a single BigInt digit on 32-bit. + -(2n ** 32n), + + -(2n ** 31n) - 1n, // One past max negative for Int32. + ]; + test(LessThan)(xs, negativeInt32); + + var xs = [ + -(2n ** 31n), // Max negative for Int32. + ]; + test(Equal)(xs, [-2147483648]); + test(LessThan)(xs, [-2147483647, -1]); + + var xs = [ + -(2n ** 31n) + 1n, + ]; + test(GreaterThan)(xs, [-2147483648]); + test(Equal)(xs, [-2147483647]); + test(LessThan)(xs, [-1]); + + var xs = [ + -1n, + ]; + test(GreaterThan)(xs, [-2147483648, -2147483647]); + test(Equal)(xs, [-1]); +} +testNegative(); + +// Test when both numbers are positive (and BigInt strictly positive). +function testPositive() { + var xs = [ + 1n, + ]; + test(GreaterThan)(xs, [0]); + test(Equal)(xs, [1]); + test(LessThan)(xs, [2147483646, 2147483647]); + + var xs = [ + 2n ** 31n - 2n, + ]; + test(GreaterThan)(xs, [0, 1]); + test(Equal)(xs, [2147483646]); + test(LessThan)(xs, [2147483647]); + + var xs = [ + 2n ** 31n - 1n, // Max positive for Int32. + ]; + test(GreaterThan)(xs, [0, 1, 2147483646]); + test(Equal)(xs, [2147483647]); + + var xs = [ + 2n ** 31n, // One past max positive for Int32. + + 2n ** 32n - 2n, + 2n ** 32n - 1n, // Max positive using a single BigInt digit on 32-bit. + 2n ** 32n, + + 2n ** 64n - 2n, + 2n ** 64n - 1n, // Max positive using a single BigInt digit on 64-bit. + 2n ** 64n, + ]; + test(GreaterThan)(xs, zeroOrPositiveInt32); +} +testPositive(); + +// Test negative BigInt and positive Int32. +function testNegativePositive() { + var xs = [ + -(2n ** 64n) - 2n, + -(2n ** 64n) - 1n, // Max negative using a single BigInt digit on 64-bit. + -(2n ** 64n), + + -(2n ** 32n) - 2n, + -(2n ** 32n) - 1n, // Max negative using a single BigInt digit on 32-bit. + -(2n ** 32n), + + -(2n ** 31n) - 1n, + -(2n ** 31n), // Max negative for Int32. + -(2n ** 31n) + 1n, + + -2n, // Extra entry to ensure assertAllCombinationsTested passes. + -1n, + ]; + test(LessThan)(xs, zeroOrPositiveInt32); +} +testNegativePositive(); + +// Test (strictly) positive BigInt and negative Int32. +function testPositiveNegative() { + var xs = [ + 1n, + + 2n ** 31n - 2n, + 2n ** 31n - 1n, // Max positive for Int32. + 2n ** 31n, + + 2n ** 32n - 2n, + 2n ** 32n - 1n, // Max positive using a single BigInt digit on 32-bit. + 2n ** 32n, + + 2n ** 64n - 2n, + 2n ** 64n - 1n, // Max positive using a single BigInt digit on 64-bit. + 2n ** 64n, + ]; + test(GreaterThan)(xs, negativeInt32); +} +testPositiveNegative(); diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js b/js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js new file mode 100644 index 0000000000..9314b88747 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js @@ -0,0 +1,82 @@ +// Test relational comparison when one operand is null or undefined. + +function test(xs) { + for (let i = 0; i < 200; ++i) { + let x = xs[i % xs.length]; + + // The result is equal when compared to the result with explicit ToNumber conversions. + + // Test when null-or-undefined is on the right-hand side. + assertEq(x < nullOrUndef, x < (+nullOrUndef)); + assertEq(x <= nullOrUndef, x <= (+nullOrUndef)); + assertEq(x >= nullOrUndef, x >= (+nullOrUndef)); + assertEq(x > nullOrUndef, x > (+nullOrUndef)); + + // Test when null-or-undefined is on the left-hand side. + assertEq(nullOrUndef < x, (+nullOrUndef) < x); + assertEq(nullOrUndef <= x, (+nullOrUndef) <= x); + assertEq(nullOrUndef >= x, (+nullOrUndef) >= x); + assertEq(nullOrUndef > x, (+nullOrUndef) > x); + } +} + +function runTest(inputs) { + let fNull = Function(`return ${test}`.replaceAll("nullOrUndef", "null"))(); + fNull(inputs); + + let fUndefined = Function(`return ${test}`.replaceAll("nullOrUndef", "undefined"))(); + fUndefined(inputs); +} + +// BigInt inputs +runTest([ + // Definitely heap digits. + -(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, +]); diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-number.js b/js/src/jit-test/tests/cacheir/bigint-compare-number.js new file mode 100644 index 0000000000..9eefaf5053 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-compare-number.js @@ -0,0 +1,205 @@ +// Same test as bigint-compare-double, except that the inputs are allowed to be any number, i.e. +// either Int32 or Double. + +// Test various combinations of (BigInt R Number) and ensures the result is consistent with +// (Number R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R Number), +// but merely ensures the possible combinations are properly handled in CacheIR. + +var xs = [ + // 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, +]; + +// Compute the Number approximation of the BigInt values. +var ys = xs.map(x => Number(x)); + +// Compute if the Number approximation of the BigInt values is exact. +// (The larger test values are all powers of two, so we can keep this function simple.) +var zs = xs.map(x => { + var isNegative = x < 0n; + if (isNegative) { + x = -x; + } + var s = x.toString(2); + if (s.length <= 53 || (s.length <= 1024 && /^1+0+$/.test(s))) { + return 0; + } + if (s.length <= 1024 && /^1+$/.test(s)) { + return isNegative ? -1 : 1; + } + if (s.length <= 1024 && /^1+0+1$/.test(s)) { + return isNegative ? 1 : -1; + } + return NaN; +}); + +function testLooseEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x == y, z === 0); + assertEq(y == x, z === 0); + } +} +testLooseEqual(); + +function testLooseNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x != y, z !== 0); + assertEq(y != x, z !== 0); + } +} +testLooseNotEqual(); + +function testLessThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x < y, false); + assertEq(y < x, false); + } else if (z > 0) { + assertEq(x < y, true); + assertEq(y < x, false); + } else if (z < 0) { + assertEq(x < y, false); + assertEq(y < x, true); + } else { + assertEq(x < y, y > 0); + assertEq(y < x, y < 0); + } + } +} +testLessThan(); + +function testLessThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x <= y, true); + assertEq(y <= x, true); + } else if (z > 0) { + assertEq(x <= y, true); + assertEq(y <= x, false); + } else if (z < 0) { + assertEq(x <= y, false); + assertEq(y <= x, true); + } else { + assertEq(x <= y, y > 0); + assertEq(y <= x, y < 0); + } + } +} +testLessThanEquals(); + +function testGreaterThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x > y, false); + assertEq(y > x, false); + } else if (z > 0) { + assertEq(x > y, false); + assertEq(y > x, true); + } else if (z < 0) { + assertEq(x > y, true); + assertEq(y > x, false); + } else { + assertEq(x > y, y < 0); + assertEq(y > x, y > 0); + } + } +} +testGreaterThan(); + +function testGreaterThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + if (z === 0) { + assertEq(x >= y, true); + assertEq(y >= x, true); + } else if (z > 0) { + assertEq(x >= y, false); + assertEq(y >= x, true); + } else if (z < 0) { + assertEq(x >= y, true); + assertEq(y >= x, false); + } else { + assertEq(x >= y, y < 0); + assertEq(y >= x, y > 0); + } + } +} +testGreaterThanEquals(); diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-string.js b/js/src/jit-test/tests/cacheir/bigint-compare-string.js new file mode 100644 index 0000000000..c5937af6aa --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-compare-string.js @@ -0,0 +1,189 @@ +// Test various combinations of (BigInt R String) and ensures the result is consistent with +// (String R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R String), +// but merely ensures the possible combinations are properly handled in CacheIR. + +var xs = [ + // One before the minimum + -(2n ** 2000n) - 1n, + + // 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, + + // One past the maximum + (2n ** 2000n) + 1n, +]; + +function idx(i) { + return (i % (xs.length - 2)) + 1; +} + +var ys = xs.map(x => String(x)); + +function testLooseEqual() { + for (var i = 0; i < 100; ++i) { + var j = idx(i); + var x = xs[j]; + var y = ys[j]; + var u = ys[j - 1]; + var v = ys[j + 1]; + + assertEq(x == y, true); + assertEq(y == x, true); + + assertEq(x == u, false); + assertEq(u == x, false); + + assertEq(x == v, false); + assertEq(v == x, false); + } +} +testLooseEqual(); + +function testLooseNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = idx(i); + var x = xs[j]; + var y = ys[j]; + var u = ys[j - 1]; + var v = ys[j + 1]; + + assertEq(x != y, false); + assertEq(y != x, false); + + assertEq(x != u, true); + assertEq(u != x, true); + + assertEq(x != v, true); + assertEq(v != x, true); + } +} +testLooseNotEqual(); + +function testLessThan() { + for (var i = 0; i < 100; ++i) { + var j = idx(i); + var x = xs[j]; + var y = ys[j]; + var u = ys[j - 1]; + var v = ys[j + 1]; + + assertEq(x < y, false); + assertEq(y < x, false); + + assertEq(x < u, false); + assertEq(u < x, true); + + assertEq(x < v, true); + assertEq(v < x, false); + } +} +testLessThan(); + +function testLessThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = idx(i); + var x = xs[j]; + var y = ys[j]; + var u = ys[j - 1]; + var v = ys[j + 1]; + + assertEq(x <= y, true); + assertEq(y <= x, true); + + assertEq(x <= u, false); + assertEq(u <= x, true); + + assertEq(x <= v, true); + assertEq(v <= x, false); + } +} +testLessThanEquals(); + +function testGreaterThan() { + for (var i = 0; i < 100; ++i) { + var j = idx(i); + var x = xs[j]; + var y = ys[j]; + var u = ys[j - 1]; + var v = ys[j + 1]; + + assertEq(x > y, false); + assertEq(y > x, false); + + assertEq(x > u, true); + assertEq(u > x, false); + + assertEq(x > v, false); + assertEq(v > x, true); + } +} +testGreaterThan(); + +function testGreaterThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = idx(i); + var x = xs[j]; + var y = ys[j]; + var u = ys[j - 1]; + var v = ys[j + 1]; + + assertEq(x >= y, true); + assertEq(y >= x, true); + + assertEq(x >= u, true); + assertEq(u >= x, false); + + assertEq(x >= v, false); + assertEq(v >= x, true); + } +} +testGreaterThanEquals(); diff --git a/js/src/jit-test/tests/cacheir/bigint-compare.js b/js/src/jit-test/tests/cacheir/bigint-compare.js new file mode 100644 index 0000000000..a67d2722d3 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-compare.js @@ -0,0 +1,163 @@ +// Test various combinations of (BigInt R BigInt) and ensures the result is consistent with +// (BigInt R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R BigInt), +// but merely ensures the possible combinations are properly handled in CacheIR. + +var xs = [ + // Definitely heap digits. + -(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, +]; + +function testLooseEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isZero = j === xs.length >> 1; + + assertEq(x == x, true); + assertEq(x == 0n, isZero); + assertEq(0n == x, isZero); + } +} +testLooseEqual(); + +function testStrictEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isZero = j === xs.length >> 1; + + assertEq(x === x, true); + assertEq(x === 0n, isZero); + assertEq(0n === x, isZero); + } +} +testStrictEqual(); + +function testLooseNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isZero = j === xs.length >> 1; + + assertEq(x != x, false); + assertEq(x != 0n, !isZero); + assertEq(0n != x, !isZero); + } +} +testLooseNotEqual(); + +function testStrictNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isZero = j === xs.length >> 1; + + assertEq(x !== x, false); + assertEq(x !== 0n, !isZero); + assertEq(0n !== x, !isZero); + } +} +testStrictNotEqual(); + +function testLessThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isNegative = j < (xs.length >> 1); + var isPositive = j > (xs.length >> 1); + + assertEq(x < x, false); + assertEq(x < 0n, isNegative); + assertEq(0n < x, isPositive); + } +} +testLessThan(); + +function testLessThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isNegativeOrZero = j <= (xs.length >> 1); + var isPositiveOrZero = j >= (xs.length >> 1); + + assertEq(x <= x, true); + assertEq(x <= 0n, isNegativeOrZero); + assertEq(0n <= x, isPositiveOrZero); + } +} +testLessThanEquals(); + +function testGreaterThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isNegative = j < (xs.length >> 1); + var isPositive = j > (xs.length >> 1); + + assertEq(x > x, false); + assertEq(x > 0n, isPositive); + assertEq(0n > x, isNegative); + } +} +testGreaterThan(); + +function testGreaterThanEquals() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var isNegativeOrZero = j <= (xs.length >> 1); + var isPositiveOrZero = j >= (xs.length >> 1); + + assertEq(x >= x, true); + assertEq(x >= 0n, isPositiveOrZero); + assertEq(0n >= x, isNegativeOrZero); + } +} +testGreaterThanEquals(); diff --git a/js/src/jit-test/tests/cacheir/bigint-tobool.js b/js/src/jit-test/tests/cacheir/bigint-tobool.js new file mode 100644 index 0000000000..806cb55731 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-tobool.js @@ -0,0 +1,72 @@ +var xs = [ + // Definitely heap digits. + -(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 + 2147483649n, + 2147483648n, + 2147483647n, + + // 2**32 + 4294967297n, + 4294967296n, + 4294967295n, + + // 2n**63n + 9223372036854775809n, + 9223372036854775808n, + 9223372036854775807n, + + // 2n**64n + 18446744073709551617n, + 18446744073709551616n, + 18446744073709551615n, + + // Definitely heap digits. + 2n ** 1000n, +]; + +function test() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + // Implicit ToBool(x) + var r = x ? true : false; + assertEq(r, x !== 0n); + } +} +test(); + +function testNot() { + for (var i = 0; i < 100; ++i) { + var x = xs[i % xs.length]; + + var r = !x; + assertEq(r, x === 0n); + } +} +testNot(); diff --git a/js/src/jit-test/tests/cacheir/bigint-unary.js b/js/src/jit-test/tests/cacheir/bigint-unary.js new file mode 100644 index 0000000000..75e67a7d53 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bigint-unary.js @@ -0,0 +1,135 @@ +var xs = [ + // Definitely heap digits. + -(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, +]; + +function testNeg() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = xs[xs.length - 1 - j]; + + assertEq(-x, y); + } +} +testNeg(); + +function testBitNot() { + var ys = xs.map(x => -(x + 1n)); + + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + assertEq(~x, y); + } +} +testBitNot(); + +function testPreInc() { + var ys = xs.map(x => x + 1n); + + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + var r = ++x; + assertEq(x, y); + assertEq(r, y); + } +} +testPostInc(); + +function testPostInc() { + var ys = xs.map(x => x + 1n); + + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + var r = x++; + assertEq(x, y); + assertEq(r, xs[j]); + } +} +testPostInc(); + +function testPreDec() { + var ys = xs.map(x => x - 1n); + + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + var r = --x; + assertEq(x, y); + assertEq(r, y); + } +} +testPostDec(); + +function testPostDec() { + var ys = xs.map(x => x - 1n); + + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + var r = x--; + assertEq(x, y); + assertEq(r, xs[j]); + } +} +testPostDec(); diff --git a/js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js b/js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js new file mode 100644 index 0000000000..0801606808 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js @@ -0,0 +1,70 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +function runTest(dividend, divisor) { + function test(dividend, divisor, expected) { + for (var i = 0; i < dividend.length; i++) { + var x = dividend[i]; + for (var j = 0; j < divisor.length; j++) { + var y = divisor[j]; + var result = x % y; + + assertEq(result, expected[i * divisor.length + j]); + } + } + } + + var f = Function(`return ${test}`)(); + + // Seed the IC to ensure we're using Int32Mod. + var ones = Array(8).fill(1); + var zeros = Array(8 * 8).fill(0); + for (var i = 0; i < 2; ++i) { + f(ones, ones, zeros); + } + + var expected = dividend.map(x => divisor.map(y => x % y)).flat(); + for (var i = 0; i < 10; ++i) { + f(dividend, divisor, expected); + } +} + +const positiveInt32 = [ + 1, 2, 3, 4, 5, 6, 7, 8, + 2**31 - 8, + 2**31 - 7, + 2**31 - 6, + 2**31 - 5, + 2**31 - 4, + 2**31 - 3, + 2**31 - 2, + 2**31 - 1, +]; + +const negativeInt32 = [ + -1, -2, -3, -4, -5, -6, -7, -8, + -(2**31 - 8), + -(2**31 - 7), + -(2**31 - 6), + -(2**31 - 5), + -(2**31 - 4), + -(2**31 - 3), + -(2**31 - 2), + -(2**31 - 1), + -(2**31), +]; + +const zero = [0]; + +const cases = [positiveInt32, zero, negativeInt32]; + +for (let a of cases) { + for (let b of cases) { + runTest(a, b); + } +} + +// Test some "interesting" cases. + +// |-2147483648 % -1| may internally compute |-2147483648 / -1|, which isn't an int32 and +// may trigger a floating point exception on some architectures. +runTest([-2147483648], [-1]); diff --git a/js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js b/js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js new file mode 100644 index 0000000000..140db26572 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js @@ -0,0 +1,75 @@ +// Pass |null| as argument to be more robust against code folding. +function testNullWithInt32OrBool(nullVal) { + var vals = [0, 1, true, false, null]; + for (var v of vals) { + assertEq(v + nullVal, Number(v)); + assertEq(v - nullVal, Number(v)); + assertEq(v * nullVal, 0); + var res = v / nullVal; + assertEq(isNaN(res) || res === Infinity, true); + assertEq(v % nullVal, NaN); + assertEq(v ** nullVal, 1); + + assertEq(nullVal + v, Number(v)); + assertEq(nullVal - v, 0 - Number(v)); + assertEq(nullVal * v, 0); + res = nullVal / v; + assertEq(isNaN(res) || res === 0, true); + res = nullVal % v; + assertEq(isNaN(res) || res === 0, true); + res = nullVal ** v; + assertEq(res === 0 || res === 1, true); + } +} +for (var i = 0; i < 15; i++) { + testNullWithInt32OrBool(null); +} + +function testUndefinedWithOther(undefinedVal) { + var vals = [1.1, NaN, true, false, null, undefined]; + for (var v of vals) { + assertEq(v + undefinedVal, NaN); + assertEq(v - undefinedVal, NaN); + assertEq(v * undefinedVal, NaN); + assertEq(v / undefinedVal, NaN); + assertEq(v % undefinedVal, NaN); + assertEq(v ** undefinedVal, NaN); + + assertEq(undefinedVal + v, NaN); + assertEq(undefinedVal - v, NaN); + assertEq(undefinedVal * v, NaN); + assertEq(undefinedVal / v, NaN); + assertEq(undefinedVal % v, NaN); + var res = undefinedVal ** v; + if (v === false || v === null) { + assertEq(res, 1); + } else { + assertEq(res, NaN); + } + } +} +for (var i = 0; i < 15; i++) { + testUndefinedWithOther(undefined); +} + +function testBooleanWithDouble(trueVal, falseVal) { + var vals = [1.1, 2.2, 5, 6, 3.14]; + for (var v of vals) { + assertEq(v + falseVal, v); + assertEq(v - falseVal, v); + assertEq(v * falseVal, 0); + assertEq(v / falseVal, Infinity); + assertEq(v % falseVal, NaN); + assertEq(v ** falseVal, 1); + + assertEq(trueVal + v, v + 1); + assertEq(trueVal - v, 1 - v); + assertEq(trueVal * v, v); + assertEq(trueVal / v, 1 / v); + assertEq(trueVal % v, 1); + assertEq(trueVal ** v, 1); + } +} +for (var i = 0; i < 15; i++) { + testBooleanWithDouble(true, false); +} diff --git a/js/src/jit-test/tests/cacheir/binaryarith.js b/js/src/jit-test/tests/cacheir/binaryarith.js new file mode 100644 index 0000000000..1746aad963 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/binaryarith.js @@ -0,0 +1,322 @@ +setJitCompilerOption('ion.forceinlineCaches', 1); + +function warmup(fun, input_array) { + for (var index = 0; index < input_array.length; index++) { + input = input_array[index]; + input_lhs = input[0]; + input_rhs = input[1]; + output = input[2]; + for (var i = 0; i < 30; i++) { + var y = fun(input_lhs, input_rhs); + assertEq(y, output) + } + } +} + + +// Add: Int32 + Int32 Overflow +var funAdd1 = (a, b) => { return a + b; } +warmup(funAdd1, [[1,2, 3], [3,4, 7], [4294967295, 2, 4294967297]]); + +// Add: Doubles +var funAdd2 = (a, b) => { return a + b; } +warmup(funAdd2, [[1.2, 2, 3.2], [3.5, 4, 7.5], [4294967295.1, 2, 4294967297.1]]); + +// Add: Type Change Int32 -> Double +var funAdd3 = (a, b) => { return a + b; } +warmup(funAdd3, [[1, 2, 3], [3, 4, 7], [4294967295, 2, 4294967297], [1.2, 2, 3.2]]); + +//Add: String Concat +var funAdd4 = (a, b) => { return a + b; } +warmup(funAdd4, [["","a","a"], ["ab","ba","abba"], ["123","456","123456"]]) + +function D(name) { this.name = name; } +D.prototype.toString = function stringify() { + return this.name; +} +obj1 = new D('A'); + +// Add: String Object Concat +var funAdd4 = (a, b) => { return a + b; } +warmup(funAdd4, [["x", obj1, "xA"], [obj1, "bba", "Abba"]]); + +// Add: Int32 Boolean +var funAdd5 = (a, b) => { return a + b; } +warmup(funAdd5, [[true, 10, 11], [false, 1, 1], [10, true, 11], [1, false, 1], + [2147483647, true, 2147483648],[true, 2147483647, 2147483648]]); + +// Add: String Number Concat +var funAdd6 = (a, b) => { return a + b; } +warmup(funAdd6, [["x", 10, "x10"], [10, "bba", "10bba"], ["x", 1.2, "x1.2"], + [1.2, "bba", "1.2bba"]]); + +// Add: String Boolean +var funAddStrBool = (a, b) => { return a + b; } +warmup(funAddStrBool, [[true, "true", "truetrue"], [false, "true", "falsetrue"], + ["a string", true, "a stringtrue"]]); + +// Sub Int32 +var funSub1 = (a, b) => { return a - b; } +warmup(funSub1, [[7, 0, 7], [7, 8, -1], [4294967295, 2, 4294967293], [0,0,0]]); + +// Sub Double +var funSub2 = (a, b) => { return a - b; } +warmup(funSub2, [[7.5, 0, 7.5], [7, 8.125, -1.125], [4294967295.3125, 2, 4294967293.3125], [NaN,10,NaN]]); + +// Sub Int32 + Boolean +var funSub3 = (a, b) => { return a - b; } +warmup(funSub3, [[7, false, 7], [7, true, 6], [false, 1, -1], [true,1,0]]); + + + +// Mul: Int32+ Int32 Overflow +var funMul1 = (a, b) => { return a * b; } +warmup(funMul1, [[1, 2, 2], [10, 21, 210], [3, 4, 12], [2147483649, 2, 4294967298], [1073741824, 1024, 1099511627776 ]]); + +// Mul: Doubles +var funMul2 = (a, b) => { return a * b; } +warmup(funMul2, [[3/32, 32, 3], [16/64, 32, 8], [3.0, 1.0, 3], [-1, 0, -0], [0, -20, -0]]); + +// Mul: Type change Int32 -> Double +var funMul3 = (a, b) => { return a * b; } +warmup(funMul3, [[1,2, 2], [10, 21, 210], [3, 4, 12], [63/32, 32, 63], [16/64, 32, 8]]); + +// Mul: Boolean +var funMul1 = (a, b) => { return a * b; } +warmup(funMul1, [[1, true, 1], [10, false, 0], [false, 4, 0], [2147483640, true, 2147483640]]); + +//Div: Int32 +var funDiv1 = (a,b) => { return a / b;} +warmup(funDiv1,[[8, 4, 2], [16, 32, 0.5], [10, 0, Infinity], [0, 0, NaN]]); + +//Div: Double +var funDiv2 = (a,b) => { return a / b;} +warmup(funDiv2, [[8.8, 4, 2.2], [16.8, 32, 0.525], [10, 0.5, 20]]); + +//Div: Type change Int32 -> Double +var funDiv3 = (a,b) => { return a / b;} +warmup(funDiv1, [[8, 4, 2], [16, 32, 0.5], [10, 0, Infinity], [0, 0, NaN], [8.8, 4, 2.2], + [16.8, 32, 0.525], [10, 0.5, 20]]); + +//Div: Boolean w/ Int32 +var funDiv4 = (a,b) => { return a / b;} +warmup(funDiv4,[[8, true, 8], [true, 2, 0.5], [10, false, Infinity], [false, false, NaN]]); + +//Mod: Int32 +var funMod1 = (a,b) => {return a % b}; +warmup(funMod1, [[8, 4, 0], [9, 4, 1], [-1, 2, -1], [4294967297, 2, 1], + [10, -3, 1]]); + +//Mod: Double +var funMod2 = (a,b) => {return a % b}; +warmup(funMod2, [[8.5, 1, 0.5], [9.5, 0.5, 0], [-0.03125, 0.0625, -0.03125], [1.64, 1.16, 0.48]]); + +//Mod: Type change Int32 -> Double +var funMod3 = (a,b) => {return a % b}; +warmup(funMod3, [[10, 0, NaN], [8, 4, 0], [9, 4, 1], [-1, 2, -1], [4294967297, 2, 1], + [8.5, 1, 0.5], [9.5, 0.5, 0], [-0.03125, 0.0625, -0.03125], + [1.64, 1.16, 0.48]]); + +//Mod: Boolean w/ Int32 +var funMod4 = (a,b) => {return a % b}; +warmup(funMod4, [[10, false, NaN], [8, true, 0], [false, 4, 0], [true, 2, 1]]); + +//Pow: Int32 +var funPow1 = (a,b) => {return a ** b}; +warmup(funPow1, [[8, 4, 4096], [9, 4, 6561], [-1, 2, 1], [2, -10000, 0], + [-3, 3, -27]]); + +//Pow: Double +var funPow2 = (a,b) => {return a ** b}; +warmup(funPow2, [[8.5, 1, 8.5], [16, 0.5, 4], [4.5, 5, 1845.28125], [18.0625, 0.5, 4.25], + [4, -1, 0.25]]); + +//Pow: Type change Int32 -> Double +var funPow3 = (a,b) => {return a ** b}; +warmup(funPow3, [[10, 0, 1], [8, 4, 4096], [9, 4, 6561], [-1, 2, 1], [2, -10000, 0], + [8.5, 1, 8.5], [16, 0.5, 4], [4.5, 5, 1845.28125], [18.0625, 0.5, 4.25], + [4, -1, 0.25]]); + +//Pow: Boolean w/ Int32 +var funPow4 = (a,b) => {return a ** b}; +warmup(funPow4, [[10, 2, 100], [8, true, 8], [false, 4, 0], [true, 2, 1]]); + +//BitOr Int32 +var funBitOr1 = (a, b) => { return a | b; } +warmup(funBitOr1, [[1, 1, 1], [8, 1, 9], [0, 1233, 1233], [5, 0, 5], + [4294967295, 123231, -1], [2147483647, 1243524, 2147483647]]); + +//BitOr Boolean w/ Int32 +var funBitOr3 = (a, b) => { return a | b; } +warmup(funBitOr3, [[1, true, 1], [8, true, 9], [false, 1233, 1233], [5, false, 5]]); + +//BitOr null w/ Int32 +var funBitOr4 = (a, b) => { return a | b; } +warmup(funBitOr4, [[1, null, 1], [8, null, 8], [null, 1233, 1233], [5, null, 5]]); + +//BitOr undefined w/ Int32 +var funBitOr5 = (a, b) => { return a | b; } +warmup(funBitOr5, [[1, void 0, 1], [8, void 0, 8], [void 0, 1233, 1233], [5, void 0, 5]]); + +//BitXOr Int32 +var funBitXOr1 = (a, b) => { return a ^ b; } +warmup(funBitXOr1, [[1, 1, 0], [5, 1, 4], [63, 31, 32], [4294967295, 2147483647, -2147483648 ] ]); + +//BitXOr Int32 +var funBitXOr2 = (a, b) => { return a ^ b; } +warmup(funBitXOr2, [[1, true, 0], [5, true, 4], [5, false, 5], [false, 1, 1]]); + +//BitXOr Double+int32 +var funBitXOr3 = (a, b) => { return a ^ b; } +warmup(funBitXOr3, [[1.3, 1, 0], [5, 1.4, 4], [63.1, 31, 32], [4294967295.9, 2147483647, -2147483648 ] ]); + +//BitXOr Number Number +var funBitXOr4 = (a, b) => { return a ^ b; } +warmup(funBitXOr4, [[54772703898, 2890608493, 1828589047], + [-54772703898, 2890608493, -1828589045], + [18446744073709551615, 54772703898, -1061870950], //UINT64 Max + [-18446744073709551615, 54772703898, -1061870950], + [4294967295, -1, 0]]); + +//BitXOr null+int32 +var funBitXOr5 = (a, b) => { return a ^ b; } +warmup(funBitXOr5, [[1, null, 1], [5, null, 5], [5, null, 5], [null, 1, 1]]); + +//BitXOr undefined+int32 +var funBitXOr6 = (a, b) => { return a ^ b; } +warmup(funBitXOr6, [[1, void 0, 1], [5, void 0, 5], [5, void 0, 5], [void 0, 1, 1]]); + +//BitAnd Int32 +var funBitAnd1 = (a, b) => { return a & b; } +warmup(funBitAnd1, [[1,1,1], [5,1,1], [63,31,31], [4294967295,2147483647,2147483647], + [-2,10,10], [-15,-2,-16], [4,128,0]]); +//BitAnd Double w/ Int32 +var funBitAnd2 = (a, b) => { return a & b; } +warmup(funBitAnd2, [[1.2 ,1, 1], [5, 1.4, 1], [63,31.99,31], + [4294967295.98,2147483647,2147483647], + [-2.9, 10, 10], [-15,-2.9,-16], [4,128.01,0]]); + +//BitAnd Int32 + Boolean +var funBitAnd1 = (a, b) => { return a & b; } +warmup(funBitAnd1, [[1,true,1], [5,false,0], [true, 6, 0], [false, 12, 0]]); + +//BitAnd Int32 + null +var funBitAnd4 = (a, b) => { return a & b; } +warmup(funBitAnd4, [[1, null, 0], [5, null, 0], [null, 6, 0], [null, 12, 0]]); + +//BitAnd Int32 + undefined +var funBitAnd5 = (a, b) => { return a & b; } +warmup(funBitAnd5, [[1, void 0, 0], [5, void 0, 0], [void 0, 6, 0], [void 0, 12, 0]]); + +//Lsh Int32 +var funLsh1 = (a, b) => { return a << b; } +warmup(funLsh1, [[5,1,10], [1,1,2], [63,31,-2147483648], + [4294967295,2147483647,-2147483648], [-2,10,-2048], [-15,-2,1073741824], + [4,128,4], [1,10,1024], [1024,2,4096]]); + +//Lsh Boolean w/ Int32 +var funLsh2 = (a, b) => { return a << b; } +warmup(funLsh2, [[5,true,10], [true,1,2], [63,false,63], [false, 12, 0]]); + +//Lsh Number Number +var funLsh3 = (a, b) => { return a << b; } +warmup(funLsh3, [[54772703898, 1, -2123741900],[2147483658, 0, -2147483638]]); + +//Lsh Boolean w/ null +var funLsh4 = (a, b) => { return a << b; } +warmup(funLsh4, [[5, null, 5], [null, 1, 0], [63, null, 63], [null, 12, 0]]); + +//Lsh Boolean w/ undefined +var funLsh5 = (a, b) => { return a << b; } +warmup(funLsh5, [[5, void 0, 5], [void 0, 1, 0], [63, void 0, 63], [void 0, 12, 0]]); + +//Rsh Int32 +var funRsh1 = (a, b) => { return a >> b; } +warmup(funRsh1, [[1,1,0], [5,1,2], [63,31,0], [4294967295,2147483647,-1], [-2,10,-1], + [-15,-2,-1], [4,128,4], [1,10,0], [1024,2,256]]); + +//Rsh Int32 +var funRsh2 = (a, b) => { return a >> b; } +warmup(funRsh2, [[true,1,0], [1,true,0], [false, 3, 0], [3, false, 3]]); + +//Rsh Number Number +var funRsh3 = (a, b) => { return a >> b; } +warmup(funRsh3, [[54772703898, 11, -518492 ], [2147483658, 0, -2147483638]]); + +//Rsh Int32 null +var funRsh4 = (a, b) => { return a >> b; } +warmup(funRsh4, [[null, 1, 0], [1, null, 1], [null, 3, 0], [3, null, 3]]); + +//Rsh Int32 undefined +var funRsh5 = (a, b) => { return a >> b; } +warmup(funRsh5, [[void 0, 1, 0], [1, void 0, 1], [void 0, 3, 0], [3, void 0, 3]]); + +//URsh Int32 +var funURsh1 = (a, b) => { return a >>> b; } +warmup(funURsh1, [[1,1,0], [5,1,2], [63,31,0], [4294967295,2147483647,1], [-2,10,4194303], + [-15,-2,3], [4,128,4], [1,10,0], [1024,2,256], [0, -6, 0], [0, 6, 0], + [0x55005500, 2, 0x15401540]]); + +//URsh Boolean Int32 +var funURsh2 = (a, b) => { return a >>> b; } +warmup(funURsh2, [[true,1,0], [5,true,2], [63,false,63], [false, 20, 0]]); + +//URsh Int32 +var funURsh3 = (a, b) => { return a >>> b; } +warmup(funURsh3, [[4294967295, 0, 4294967295]]); + +//URsh Number Number +var funURsh4 = (a, b) => { return a >>> b; } +warmup(funURsh4, [[54772703898, 11, 1578660], [2147483658, 11, 1048576], + [4294967295, 0, 4294967295]]); + +//URsh null Int32 +var funURsh5 = (a, b) => { return a >>> b; } +warmup(funURsh5, [[null, 1, 0], [5, null, 5], [63, null, 63], [null, 20, 0]]); + +//URsh undefined Int32 +var funURsh6 = (a, b) => { return a >>> b; } +warmup(funURsh6, [[void 0, 1, 0], [5, void 0, 5], [63, void 0, 63], [void 0, 20, 0]]); + + +// Other Test cases that Have been useful: +for (var k=0; k < 30; k++) { + A="01234567"; + res ="" + for (var i = 0; i < 8; ++i) { + var v = A[7 - i]; + res+=v; + } + assertEq(res, "76543210"); +} + +// Begin OOM testing: +if (!('oomTest' in this)) + quit(); + +// Add: String Number Concat OOM test +var addOom = (a, b) => { return a + b; } + +function generate_digits(prefix, start) { + digits = [] + number = ""+start+".25"; + for (var i = 1; i < 7; i++) { + number = i + number; + digits.push([prefix, Number(number), prefix + number]); + } + return digits; +} + +// Trying to defeat dtoacache: Warm the IC with one set of digits, then actually oomTest +// using another set. +var warmup_digits = generate_digits("x", 1); +var test_digits = generate_digits("x", 2); + +function ot(digits) { + warmup(addOom, digits); +} + +// Ensure ICs are warmed +ot(warmup_digits); + +oomTest(() => { ot(test_digits); });
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/bind-function-specialized.js b/js/src/jit-test/tests/cacheir/bind-function-specialized.js new file mode 100644 index 0000000000..acac842a7f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bind-function-specialized.js @@ -0,0 +1,72 @@ +load(libdir + "asserts.js"); + +function testBasic() { + var g = function foo(a, b, c) { return a - b - c; }; + for (var i = 0; i < 100; i++) { + var bound1 = g.bind(null, 1); + assertEq(bound1.length, 2); + assertEq(bound1.name, "bound foo"); + var bound2 = bound1.bind(null, 2); + assertEq(bound2.length, 1); + assertEq(bound2.name, "bound bound foo"); + assertEq(bound2(9), -10); + } +} +testBasic(); + +function testBindNonCtor() { + var g = (a, b, c) => a - b - c; + for (var i = 0; i < 100; i++) { + var bound1 = g.bind(null, 1); + var bound2 = bound1.bind(null, 2); + assertEq(bound1(2, 3), -4); + assertEq(bound2(4), -5); + assertThrowsInstanceOf(() => new bound1(2, 3), TypeError); + assertThrowsInstanceOf(() => new bound2(4), TypeError); + assertEq(bound2.length, 1); + assertEq(bound2.name, "bound bound g"); + } +} +testBindNonCtor(); + +function testBindSelfHosted() { + var g = Array.prototype.map; + var arr = [1, 2, 3]; + for (var i = 0; i < 100; i++) { + var bound1 = g.bind(arr); + var bound2 = bound1.bind(null, x => x + 5); + assertEq(bound1(x => x + 3).toString(), "4,5,6"); + assertEq(bound2().toString(), "6,7,8"); + assertEq(bound2.length, 0); + assertEq(bound2.name, "bound bound map"); + } +} +testBindSelfHosted(); + +function testBoundDeletedName() { + var g = function foo(a, b, c) { return a - b - c; }; + var bound1 = g.bind(null); + var bound2 = g.bind(null); + delete bound2.name; + for (var i = 0; i < 100; i++) { + var obj = i < 50 ? bound1 : bound2; + var bound3 = obj.bind(null); + assertEq(bound3.length, 3); + assertEq(bound3.name, i < 50 ? "bound bound foo" : "bound "); + } +} +testBoundDeletedName(); + +function testWeirdProto() { + var g = function foo() { return 123; }; + var proto = {bind: Function.prototype.bind}; + Object.setPrototypeOf(g, proto); + for (var i = 0; i < 100; i++) { + var bound1 = g.bind(null); + assertEq(Object.getPrototypeOf(bound1), proto); + var bound2 = bound1.bind(null); + assertEq(Object.getPrototypeOf(bound2), proto); + assertEq(bound2(), 123); + } +} +testWeirdProto(); diff --git a/js/src/jit-test/tests/cacheir/bindname-lexical-errors.js b/js/src/jit-test/tests/cacheir/bindname-lexical-errors.js new file mode 100644 index 0000000000..d31eecdafb --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bindname-lexical-errors.js @@ -0,0 +1,26 @@ +const x = 1; +function testConst() { + for (var i = 0; i < 20; i++) { + try { + x = 2; + } catch (e) { + continue; + } + throw "Fail1"; + } + assertEq(x, 1); +} +testConst(); + +function testUninit() { + for (var i = 0; i < 20; i++) { + try { + y = 2; + } catch (e) { + continue; + } + throw "Fail2"; + } +} +testUninit(); +let y; diff --git a/js/src/jit-test/tests/cacheir/boolean-call.js b/js/src/jit-test/tests/cacheir/boolean-call.js new file mode 100644 index 0000000000..7c6d94f3d4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/boolean-call.js @@ -0,0 +1,67 @@ +function wrapper(values) { + function test(values) { + var expected = values.map(v => !!v); + + for (var i = 0; i < 100; ++i) { + var ix = i % values.length; + var val = values[ix]; + var actual = Boolean(val); + assertEq(actual, expected[ix]); + } + } + + for (var i = 0; i < 2; ++i) { + test(values); + } +} + +function makeTest() { + // Create a copy to avoid type pollution. + return Function(`return ${wrapper}`)(); +} + +// Use a new compartment to create a wrapper. +var g = newGlobal({newCompartment: true}); + +var testValues = { + boolean: [true, false], + int32: [-2147483648, -1, 0, 1, 2147483647], + double: [-Infinity, -1.5, -1, -0.5, -0, +0, +0.5, +1, +1.5, Infinity, NaN], + string: ["", "true", "false", "0", "1", "hello"], + symbol: [Symbol(), Symbol("desc"), Symbol.iterator], + bigint: [ + -(2n ** 1000n), + -18446744073709551616n, // -(2n**64n) + -9223372036854775808n, // -(2n**63n) + -4294967296n, // -(2**32) + -2147483648n, // -(2**31) + -1n, 0n, 1n, + 2147483648n, // 2**31 + 4294967296n, // 2**32 + 9223372036854775808n, // 2n**63n + 18446744073709551616n, // 2n**64n + 2n ** 1000n, + ], + object: [{}, [], function(){}, new Proxy({}, {}), createIsHTMLDDA(), g.eval("createIsHTMLDDA()")], + null: [null], + undefined: [undefined], +}; + +for (var values of Object.values(testValues)) { + makeTest()(values); +} + +// boolean and int32 +makeTest()([].concat(testValues.boolean, testValues.int32)); + +// int32 and double +makeTest()([].concat(testValues.int32, testValues.double)); + +// null and undefined +makeTest()([].concat(testValues.null, testValues.undefined)); + +// null, undefined, and object +makeTest()([].concat(testValues.null, testValues.undefined, testValues.object)); + +// all values +makeTest()(Object.values(testValues).flat()); diff --git a/js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js b/js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js new file mode 100644 index 0000000000..13da6000f1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js @@ -0,0 +1,149 @@ +// Test comparison between Boolean x {String, Double}. + +// Boolean R String <=> ToNumber(Boolean) R ToNumber(String) +// Boolean R Number <=> ToNumber(Boolean) R Number + +// The entries in the first halves of xs, ys, and zs should be loose-equal to each other. + +var xs = [ + // ToNumber(bool) == 0 + false, false, false, false, + + // ToNumber(bool) == 1 + true, true, true, true, + + // Filler + false, false, false, false, + true, true, true, true, +]; + +var ys = [ + // ToNumber(str) == 0 + "", "0", "0.0", ".0", + + // ToNumber(str) == 1 + "1", "1.0", "0x1", " 1\t\r\n", + + // ToNumber(str) != {0, 1} + // (Duplicate entries to ensure they're neither equal to |true| nor to |false|.) + "not-a-number", "NaN", "Infinity", "2", + "not-a-number", "NaN", "Infinity", "2", +]; + +function Double(x) { + // numberToDouble always returns a Double valued number. + return numberToDouble(x); +} + +var zs = [ + // = 0 + Double(0), Double(0), -0, -0, + + // = 1 + Double(1), Double(1), Double(1), Double(1), + + // != {0, 1} + // (Duplicate entries to ensure they're neither equal to |true| nor to |false|.) + NaN, Infinity, Double(2), Double(-1.5), + NaN, Infinity, Double(2), Double(-1.5), +]; + +function testLooseEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + var r = j < (xs.length >> 1); + + assertEq(x == y, r); + assertEq(y == x, r); + + assertEq(x == z, r); + assertEq(z == x, r); + } +} +testLooseEqual(); + +function testLooseNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + var r = j < (xs.length >> 1); + + assertEq(x != y, !r); + assertEq(y != x, !r); + + assertEq(x != z, !r); + assertEq(z != x, !r); + } +} +testLooseNotEqual(); + +function testLessThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x < y, +x < +y); + assertEq(y < x, +y < +x); + + assertEq(x < z, +x < +z); + assertEq(z < x, +z < +x); + } +} +testLessThan(); + +function testLessThanEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x <= y, +x <= +y); + assertEq(y <= x, +y <= +x); + + assertEq(x <= z, +x <= +z); + assertEq(z <= x, +z <= +x); + } +} +testLessThanEqual(); + +function testGreaterThan() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x > y, +x > +y); + assertEq(y > x, +y > +x); + + assertEq(x > z, +x > +z); + assertEq(z > x, +z > +x); + } +} +testGreaterThan(); + +function testGreaterThanEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + var z = zs[j]; + + assertEq(x >= y, +x >= +y); + assertEq(y >= x, +y >= +x); + + assertEq(x >= z, +x >= +z); + assertEq(z >= x, +z >= +x); + } +} +testGreaterThanEqual(); diff --git a/js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js b/js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js new file mode 100644 index 0000000000..321fbb47f6 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js @@ -0,0 +1,46 @@ +class Base { + constructor(b) { + this.b = b; + } +} +class Derived extends Base { + constructor(a, b) { + super(b); + this.a = a; + } +} + +function testSimple() { + var boundCtor = Derived.bind(null, 1); + for (var i = 0; i < 100; i++) { + var o = new boundCtor(2); + assertEq(o.a, 1); + assertEq(o.b, 2); + + } +} +testSimple(); + +function testMegamorphic() { + var ctors = [ + function(a, b) { this.a = a; this.b = b; this.c = 1; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 2; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 3; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 4; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 5; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 6; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 7; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 8; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 9; }.bind(null, 1), + function(a, b) { this.a = a; this.b = b; this.c = 10; }.bind(null, 1), + Derived.bind(null, 1), + Derived.bind(null, 1), + ]; + for (var i = 0; i < 100; i++) { + var ctor = ctors[i % ctors.length]; + var o = new ctor(2); + assertEq(o.a, 1); + assertEq(o.b, 2); + } +} +testMegamorphic(); diff --git a/js/src/jit-test/tests/cacheir/bound-construct-hook.js b/js/src/jit-test/tests/cacheir/bound-construct-hook.js new file mode 100644 index 0000000000..6d31db4192 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bound-construct-hook.js @@ -0,0 +1,18 @@ +function test() { + // Some bound callables that we're unlikely to optimize better in CacheIR. + var boundCtor = (new Proxy(Array, {})).bind(null, 1, 2, 3); + var boundNonCtor = (new Proxy(x => x + 1, {})).bind(null, 1, 2, 3); + + for (var i = 0; i < 60; i++) { + var fun = i < 40 ? boundCtor : boundNonCtor; + var ex = null; + try { + var res = new fun(100, 101); + assertEq(JSON.stringify(res), "[1,2,3,100,101]"); + } catch (e) { + ex = e; + } + assertEq(ex === null, i < 40); + } +} +test(); diff --git a/js/src/jit-test/tests/cacheir/bound-construct-scripted.js b/js/src/jit-test/tests/cacheir/bound-construct-scripted.js new file mode 100644 index 0000000000..056a538159 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bound-construct-scripted.js @@ -0,0 +1,72 @@ +function testNewTargetGuard() { + var weirdNewTarget = function() {}; + var fun = function() { return new.target; }; + var boundFun = fun.bind(null); + for (var i = 0; i < 60; i++) { + var newTarget = i < 40 ? boundFun : weirdNewTarget; + var res = Reflect.construct(boundFun, [], newTarget); + assertEq(res, i < 40 ? fun : weirdNewTarget); + } +} +testNewTargetGuard(); + +function testPrototypeGuard() { + var fun = function() {}; + var boundFun = fun.bind(null); + var customPrototype1 = {}; + var customPrototype2 = {}; + fun.prototype = customPrototype1; + + for (var i = 0; i < 60; i++) { + if (i === 40) { + fun.prototype = customPrototype2; + } + var res = new boundFun(); + assertEq(Object.getPrototypeOf(res), i < 40 ? customPrototype1 : customPrototype2); + } +} +testPrototypeGuard(); + +function testNonObjectPrototypeGuard() { + var fun = function() {}; + var boundFun = fun.bind(null); + fun.prototype = null; + var customPrototype = {}; + + for (var i = 0; i < 60; i++) { + if (i === 40) { + fun.prototype = customPrototype; + } + var res = new boundFun(); + assertEq(Object.getPrototypeOf(res), i < 40 ? Object.prototype : customPrototype); + } +} +testNonObjectPrototypeGuard(); + +function testObjectReturnValue() { + var fun = function() { return Math; }; + var boundFun = fun.bind(null); + for (var i = 0; i < 60; i++) { + var res = new boundFun(); + assertEq(res, Math); + } +} +testObjectReturnValue(); + +function testManyArgs() { + var fun = function(a, b, c, d, e, f, g, h, i, j) { + this.values = [a, b, c, d, e, f, g, h, i, j].join(","); + }; + var boundFun1 = fun.bind(null, 1, 2); + var boundFun2 = fun.bind(null, 1, 2, 3, 4, 5, 6); + for (var i = 0; i < 60; i++) { + assertEq(new boundFun1().values, "1,2,,,,,,,,"); + assertEq(new boundFun1(10, 11, 12, 13, 14).values, "1,2,10,11,12,13,14,,,"); + assertEq(new boundFun1(10, 11, 12, 13, 14, 15, 16, 17).values, "1,2,10,11,12,13,14,15,16,17"); + + assertEq(new boundFun2().values, "1,2,3,4,5,6,,,,"); + assertEq(new boundFun2(10, 11).values, "1,2,3,4,5,6,10,11,,"); + assertEq(new boundFun2(10, 11, 12, 13, 14, 15, 16, 17).values, "1,2,3,4,5,6,10,11,12,13"); + } +} +testManyArgs(); diff --git a/js/src/jit-test/tests/cacheir/bug1345707.js b/js/src/jit-test/tests/cacheir/bug1345707.js new file mode 100644 index 0000000000..616a5e365e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1345707.js @@ -0,0 +1,7 @@ +for (var i=0; i<10; i++) { + var o = {}; + if (i & 1) + Object.preventExtensions(o); + o[0] = i; + assertEq(0 in o, !(i & 1)); +} diff --git a/js/src/jit-test/tests/cacheir/bug1357024.js b/js/src/jit-test/tests/cacheir/bug1357024.js new file mode 100644 index 0000000000..a70f605378 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1357024.js @@ -0,0 +1,12 @@ +function f() { + var o = {}; + for (var j = 0; j < 15; j++) { + try { + o.__proto__ = o || j; + } catch(e) { + continue; + } + throw "Fail"; + } +} +f(); diff --git a/js/src/jit-test/tests/cacheir/bug1397026.js b/js/src/jit-test/tests/cacheir/bug1397026.js new file mode 100644 index 0000000000..8e2dc00339 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1397026.js @@ -0,0 +1,43 @@ +function f1() { + var o = {}; + var values = []; + for (var i = 0; i < 6; ++i) { + var desc = { + value: i, + writable: true, + configurable: true, + enumerable: true + }; + try { + Object.defineProperty(o, "p", desc); + } catch (e) { + } + if (i === 1) { + Object.defineProperty(o, "p", {configurable: false}); + } + values.push(o.p); + } + assertEq(values.toString(), "0,1,1,1,1,1"); +} +f1(); + +function f2() { + var o = {}; + for (var i = 0; i < 6; ++i) { + var desc = { + value: i, + writable: true, + configurable: true, + enumerable: true + }; + try { + Object.defineProperty(o, "p", desc); + } catch (e) { + } + assertEq(Object.getOwnPropertyDescriptor(o, "p").enumerable, true); + if (i > 0) { + Object.defineProperty(o, "p", {enumerable: false}); + } + } +} +f2(); diff --git a/js/src/jit-test/tests/cacheir/bug1414849.js b/js/src/jit-test/tests/cacheir/bug1414849.js new file mode 100644 index 0000000000..1ebe86a82a --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1414849.js @@ -0,0 +1,12 @@ +function g(s) { + for (var i = s; ; i--) { + if (s[i] !== ' ') + return; + } +} +function f() { + var s = "foo".match(/(.*)$/)[0]; + return g(s); +} +for (var i = 0; i < 10; i++) + f(); diff --git a/js/src/jit-test/tests/cacheir/bug1420910.js b/js/src/jit-test/tests/cacheir/bug1420910.js new file mode 100644 index 0000000000..41d17376dd --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1420910.js @@ -0,0 +1,35 @@ +// Testing InstanceOf IC. + +Array.prototype.sum = function() { + return this.reduce(( acc, cur ) => acc + cur, 0); +} + + +Iters = 20; + +function resultArray(fn, obj) { + res = new Array(); + for (var x = 0; x < Iters; x++) { + res.push(fn(obj) ? 1 : 0); + } + return res; +} + +// Ensure alteration of .prototype invalidates IC +function basic() {}; + +protoA = { prop1: "1"}; +basic.prototype = protoA; + +io1 = x => { return x instanceof basic; } + +var x = new basic(); +beforePrototypeModification = resultArray(io1,x).sum(); //Attach and test IC +assertEq(beforePrototypeModification,Iters); + +basic.prototype = {}; // Invalidate IC +afterPrototypeModification = resultArray(io1,x).sum(); //Test +assertEq(afterPrototypeModification,0); + +//Primitive LHS returns false. +assertEq(resultArray(io1,0).sum(),0);
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/bug1423139.js b/js/src/jit-test/tests/cacheir/bug1423139.js new file mode 100644 index 0000000000..31ad297a49 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1423139.js @@ -0,0 +1,15 @@ +var c = 0; +for (var i = 0; i < 5; i++) { + try { + Object.defineProperty([], "length", { + configurable: true, + enumerable: true, + writable: true, + value: 0 + }); + } catch (e) { + assertEq(e instanceof TypeError, true); + c++; + } +} +assertEq(c, i); diff --git a/js/src/jit-test/tests/cacheir/bug1438727.1.js b/js/src/jit-test/tests/cacheir/bug1438727.1.js new file mode 100644 index 0000000000..683fd8ab90 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1438727.1.js @@ -0,0 +1,22 @@ +let count = 0; +function testMathyFunction(f, inputs) { + for (var j = 0; j < inputs.length; ++j) + for (var k = 0; k < inputs.length; ++k) { + let a = f(inputs[j], inputs[k]); + count += 1; + print("Number " + count + ": " + a + " (inputs "+inputs[j]+","+inputs[k]+")"); + } +} +mathy0 = function(x, y) { + return (x / (y ? x : -Number.MAX_SAFE_INTEGER) > Math.fround(+y & +0x100000000) ** Math.fround(y)) +} +testMathyFunction(mathy0, /*MARR*/ [ + [1], , , [1], + [1], , [1], , , [1], , [1], , , [1], + [1], , [1], , , [1], + [1], , [1], , [1], + [1], + [1], , -1 / 0 +]) + +testMathyFunction(mathy0, [, -Number.MAX_VALUE, Number.MIN_SAFE_INTEGER, 0x100000001, 0x07fffffff, -0x07fffffff, 0 / 0, Number.MIN_VALUE, -0x0ffffffff, Number.MAX_SAFE_INTEGER, 0x0ffffffff, -0x100000000, , 1 / 0, 0x080000000, -1 / 0, 0x100000000])
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/bug1438727.2.js b/js/src/jit-test/tests/cacheir/bug1438727.2.js new file mode 100644 index 0000000000..d6941f7ea9 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1438727.2.js @@ -0,0 +1,6 @@ +var lfRandom = 0; +var i = 0; +while (i < 1000) { + lfRunTypeId = 4 + (lfRandom % 2); + i++; +} diff --git a/js/src/jit-test/tests/cacheir/bug1438727.3.js b/js/src/jit-test/tests/cacheir/bug1438727.3.js new file mode 100644 index 0000000000..0cdc9fab7c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1438727.3.js @@ -0,0 +1,16 @@ +let lfPreamble = ` +assertEq = function(a,b) { + try { print(a); print(b); } catch(exc) {} +} +`; +evaluate(lfPreamble, {}); +var a = [1, 2, 3]; +var b = [4, 5, 6]; +function testFold() { + for (var i = 0; i < 10; i++) { + var x = a[i]; + var z = b[i]; + if (((x / x) | 0) < 3) assertEq(x !== z, true); + } +} +for (var i = 0; i < 10; i++) testFold(); diff --git a/js/src/jit-test/tests/cacheir/bug1438727.4.js b/js/src/jit-test/tests/cacheir/bug1438727.4.js new file mode 100644 index 0000000000..85a9f54a84 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1438727.4.js @@ -0,0 +1,4 @@ +function f(v, e) { + for (var i = 2; i < 9; i++) v %= e; +} +f(0, 1); diff --git a/js/src/jit-test/tests/cacheir/bug1438727.js b/js/src/jit-test/tests/cacheir/bug1438727.js new file mode 100644 index 0000000000..4846195da8 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1438727.js @@ -0,0 +1,7 @@ +const handler = {} +function testArray(arr) { + let proxy = new Proxy(arr, handler) + proxy.sort((x, y) => 1 * x - y); + arr.sort((x, y) => 1 * x - y); +} +testArray([5, (this), 2, 99]);
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/bug1439180.js b/js/src/jit-test/tests/cacheir/bug1439180.js new file mode 100644 index 0000000000..90d77f3204 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1439180.js @@ -0,0 +1,9 @@ +h = function f(x) { + x = +"NaN"; + return /I/ (~x); + } +for (var j = 0; j < 3; j++) { + try { + h(); + } catch (e) {} +} diff --git a/js/src/jit-test/tests/cacheir/bug1448136.js b/js/src/jit-test/tests/cacheir/bug1448136.js new file mode 100644 index 0000000000..e08e67d80d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1448136.js @@ -0,0 +1,23 @@ +print = function(s) { return s.toString(); } +assertEq = function(a,b) { + try { print(a); print(b); } catch(exc) {} +} +g = newGlobal({newCompartment: true}); +g.parent = this; +g.eval("(" + function() { + Debugger(parent).onExceptionUnwind = function(frame) { + frame.older + } +} + ")()") +function a() {}; +function b() {}; +for (let _ of Array(100)) + assertEq(b instanceof a, true); +function c(){}; +function d(){}; +function e(){}; +Object.defineProperty(a, Symbol.hasInstance, {value: assertEq }); +let funcs = [a, b, c, d]; +for (let f of funcs) + assertEq(e instanceof f, true); + diff --git a/js/src/jit-test/tests/cacheir/bug1451976.js b/js/src/jit-test/tests/cacheir/bug1451976.js new file mode 100644 index 0000000000..565362bc37 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1451976.js @@ -0,0 +1,11 @@ +// This test case originally failed under --enable-more-deterministic + +setJitCompilerOption("ion.forceinlineCaches", 1); +function f(y, z) { + return Math.fround(z) < ~y; +}; +var x = [2 ** 53 - 2, 0]; +for (var i = 0; i < 3; ++i) { + assertEq(f(x[0], x[1]),true); +} + diff --git a/js/src/jit-test/tests/cacheir/bug1451984.js b/js/src/jit-test/tests/cacheir/bug1451984.js new file mode 100644 index 0000000000..bfbcb109ba --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1451984.js @@ -0,0 +1,10 @@ +// This test case originally failed only with --enable-more-deterministic + +setJitCompilerOption("ion.forceinlineCaches", 1); +function f(x) { + return Math.pow(Math.fround(Math.fround()), ~(x >>> 0)); +} +assertEq(f(-1),1); +assertEq(f(-1),1); +assertEq(f(-1),1); +assertEq(f(-1),1); diff --git a/js/src/jit-test/tests/cacheir/bug1459754.js b/js/src/jit-test/tests/cacheir/bug1459754.js new file mode 100644 index 0000000000..bfbe626acc --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1459754.js @@ -0,0 +1,9 @@ +function f(x) { + this["__proto__"] = x; + let tmp = this.toString; + assertEq(x === null, tmp === void 0); +} + +for (let e of [[], null, []]) { + new f(e); +} diff --git a/js/src/jit-test/tests/cacheir/bug1462280.js b/js/src/jit-test/tests/cacheir/bug1462280.js new file mode 100644 index 0000000000..d4581ad6ac --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1462280.js @@ -0,0 +1,3 @@ +for (var i = 0; i < 2; i++) { + evaluate("var obj = {[Symbol.iterator]: Symbol.iterator};"); +} diff --git a/js/src/jit-test/tests/cacheir/bug1471361.js b/js/src/jit-test/tests/cacheir/bug1471361.js new file mode 100644 index 0000000000..d50acdf558 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1471361.js @@ -0,0 +1,13 @@ +// This is a fuzzer-found crash inducing test. +// Pass is simply not crashing. +(function () { + f = function(y) { + ~+y; + } + x = [new Number]; + for (var j = 0; j < 3; ++j) { + for (var k = 0; k < 3; ++k) { + f(x[k]); + } + } +})(); diff --git a/js/src/jit-test/tests/cacheir/bug1483183.js b/js/src/jit-test/tests/cacheir/bug1483183.js new file mode 100644 index 0000000000..ec95b52a9f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1483183.js @@ -0,0 +1,8 @@ +if (!('stackTest' in this)) + quit(); + +stackTest(new Function(` +newGlobal({ + sameZoneAs: [] +}).frame; +`)); diff --git a/js/src/jit-test/tests/cacheir/bug1488786-2.js b/js/src/jit-test/tests/cacheir/bug1488786-2.js new file mode 100644 index 0000000000..7ac42f2ce4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1488786-2.js @@ -0,0 +1,27 @@ +setJitCompilerOption('ion.forceinlineCaches', 1); + +function protoChange() { + var o = {0: 0, 1: 0, 0x10000: 0, 0x20000: 0}; + + var tests = [1, 0, 0x10000, 0x20000]; + + function result_map(key, i) { + if (i > 5 && key == 0x20000) + return undefined; + return 0; + } + + for (var i = 0; i < 10; i++) { + for (var key of tests) { + assertEq(o[key], result_map(key, i)); + } + + if (i == 5) { + delete o[0x20000]; + } + } +} + +for (var i = 0; i < 10; i++) { + protoChange(); +}
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/bug1488786.js b/js/src/jit-test/tests/cacheir/bug1488786.js new file mode 100644 index 0000000000..656f1f0d2e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1488786.js @@ -0,0 +1,38 @@ +setJitCompilerOption('ion.forceinlineCaches', 1); + +var A = Array(2**18); +A[0] = "A"; +A[1] = "B"; +A[2**14] = "C"; +A[2**31-1] = "D"; +A[-1] = "E"; + +function get_thee(a,x) { + return a[x]; +} + +// Warmup IC +for (var i = 0; i < 30; i++) { + get_thee(A, i % A.length); +} + +// numberToDouble always returns a double-typed Number, so helps +// us ensure we're accessing the array with a double-typed Number index. +var y = numberToDouble(1); +var z = 2**31-1; +// Ensure we handle negative indices. +var a = -1; + +function test() { + for (var i = 0; i < 30; i++) { + assertEq(get_thee(A,y), "B"); + assertEq(get_thee(A,z), "D"); + assertEq(get_thee(A,a), "E"); + } +} +test(); + +if (!('oomTest' in this)) + quit(); + +oomTest(test); diff --git a/js/src/jit-test/tests/cacheir/bug1494537-plain.js b/js/src/jit-test/tests/cacheir/bug1494537-plain.js new file mode 100644 index 0000000000..e17cd3bd1d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1494537-plain.js @@ -0,0 +1,123 @@ +// Version of bug1494537.js for plain objects. + +let offsets = [213, 559, 255, 515, 30, 507, 252, 329, 487, 7]; + +function update_index(i, j) { + var offset = offsets[j % offsets.length]; + return i + offset; +} + +function compute_index(initial, count) { + for (var i = 0; i < count; i++) { + initial = update_index(initial, i); + } + return initial; +} + +// This is written so that the IC added in the bug activates. +function mutate_object(obj, count, epsilon = 0) { + var index = 0; + for (var i = 0; i < count; i++) { + index = update_index(index, i); + obj[index] = i + epsilon; + } + return obj[offsets[0]+offsets[1]] === (1 + epsilon) && + obj[10] === undefined; +} + +// Monomorphizing mutate_object to ensure we get the IC chains we want +function create_variant(variant) { + var source = mutate_object.toString().replace("mutate_object", "mutate_object_"+variant); + return source; +} + +function test_basic() { + eval(create_variant("basic")); + var x = {}; + + var count = 100; + assertEq(mutate_object_basic(x, count), true); + var end = compute_index(0, count); + assertEq(x[end], count - 1); + assertEq(x[end - 1], undefined); +} + +// Ensure the IC respects frozen. +function test_frozen() { + eval(create_variant("frozen")); + var x = {}; + Object.freeze(x); + + var count = 100; + assertEq(mutate_object_frozen(x, count), false); + + var end = compute_index(0, count); + + var y = {}; + assertEq(mutate_object_frozen(y, count), true); + assertEq(y[end], count - 1); + Object.freeze(y); + + // After a mutated object is frozen, can't subsequently modify elements + assertEq(mutate_object_frozen(x, count, 10), false); + assertEq(y[end], count - 1); +} + +// Let's make sure updates to the object happen as expected. +function test_update() { + eval(create_variant("update")); + + var x = {}; + var count = 100; + assertEq(mutate_object_update(x, count), true); + var end = compute_index(0, count); + assertEq(x[end], count - 1); + assertEq(x[end - 1], undefined); + + var epsilon = 2; + mutate_object_update(x, 200, epsilon); + assertEq(x[end], count -1 + epsilon) +} + +// Elements may be non-writable, let us not write them. +function test_nonwritable() { + eval(create_variant("nonwritable")); + var x = {}; + var count = 100; + var index = compute_index(0, 10); + Object.defineProperty(x, index, {value: -10, writable: false}); + mutate_object_nonwritable(x, count); + assertEq(x[index], -10); +} + +// Random indices can get setters, let's make sure we honour those. +function test_setter() { + eval(create_variant("setter")); + var x = {}; + var count = 100; + var index = compute_index(0, 80); + var sigil = 0; + Object.defineProperty(x, index, {set(newVal) {sigil++; }}); + mutate_object_setter(x, count); + assertEq(sigil, 1); + assertEq(x[index], undefined); +} + +// Ensure indexes on the prototype don't break things. +function test_proto_indices() { + eval(create_variant("proto_indices")); + var x = {}; + var count = 100; + var index = compute_index(0, 80); + x.__proto__[index] = "hello"; + mutate_object_proto_indices(x, count); + assertEq(x.__proto__[index], "hello"); + assertEq(x[index], 79); +} + +test_basic(); +test_frozen(); +test_update(); +test_nonwritable(); +test_setter(); +test_proto_indices(); diff --git a/js/src/jit-test/tests/cacheir/bug1494537.js b/js/src/jit-test/tests/cacheir/bug1494537.js new file mode 100644 index 0000000000..7bf139819c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1494537.js @@ -0,0 +1,125 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +let offsets = [213, 559, 255, 515, 30, 507, 252, 329, 487, 7]; + +function update_index(i, j) { + var offset = offsets[j % offsets.length]; + return i + offset; +} + +function compute_index(initial, count) { + for (var i = 0; i < count; i++) { + initial = update_index(initial, i); + } + return initial; +} + +// This is written so that the IC added in the bug activates. +function mutate_array(array, count, epsilon = 0) { + var index = 0; + for (var i = 0; i < count; i++) { + index = update_index(index, i); + array[index] = i + epsilon; + } + return array[offsets[0]+offsets[1]] === (1 + epsilon) && + array[10] === undefined; +} + +// Monomorphizing mutate_array to ensure we get the IC chains we want +function create_variant(variant) { + var source = mutate_array.toString().replace("mutate_array", "mutate_array_"+variant); + return source; +} + +function test_basic() { + eval(create_variant("basic")); + var x = []; + + var count = 100; + assertEq(mutate_array_basic(x, count), true); + var end = compute_index(0, count); + assertEq(x[end], count - 1); + assertEq(x[end - 1], undefined); +} + +// Ensure the IC respects frozen. +function test_frozen() { + eval(create_variant("frozen")); + var x = []; + Object.freeze(x); + + var count = 100; + assertEq(mutate_array_frozen(x, count), false); + assertEq(x.length, 0); + + var end = compute_index(0, count); + + var y = []; + assertEq(mutate_array_frozen(y, count), true); + assertEq(y[end], count - 1); + Object.freeze(y); + + // After a mutated array is frozen, can't subsequently modify elements + assertEq(mutate_array_frozen(x, count, 10), false); + assertEq(y[end], count - 1); +} + +// Let's make sure updates to the array happen as expected. +function test_update() { + eval(create_variant("update")); + + var x = []; + var count = 100; + assertEq(mutate_array_update(x, count), true); + var end = compute_index(0, count); + assertEq(x[end], count - 1); + assertEq(x[end - 1], undefined); + + var epsilon = 2; + mutate_array_update(x, 200, epsilon); + assertEq(x[end], count -1 + epsilon) +} + +// Elements may be non-writable, let us not write them. +function test_nonwritable() { + eval(create_variant("nonwritable")); + var x = []; + var count = 100; + var index = compute_index(0, 10); + Object.defineProperty(x, index, {value: -10, writable: false}); + mutate_array_nonwritable(x, count); + assertEq(x[index], -10); +} + +// Random indices can get setters, let's make sure we honour those. +function test_setter() { + eval(create_variant("setter")); + var x = []; + var count = 100; + var index = compute_index(0, 80); + var sigil = 0; + Object.defineProperty(x, index, {set(newVal) {sigil++; }}); + mutate_array_setter(x, count); + assertEq(sigil, 1); + assertEq(x[index], undefined); +} + +// Ensure indexes on the prototype don't break things; +// +function test_proto_indices() { + eval(create_variant("proto_indices")); + var x = []; + var count = 100; + var index = compute_index(0, 80); + x.__proto__[index] = "hello"; + mutate_array_proto_indices(x, count); + assertEq(x.__proto__[index], "hello"); + assertEq(x[index], 79); +} + +test_basic(); +test_frozen(); +test_update(); +test_nonwritable(); +test_setter(); +test_proto_indices(); diff --git a/js/src/jit-test/tests/cacheir/bug1500255.js b/js/src/jit-test/tests/cacheir/bug1500255.js new file mode 100644 index 0000000000..41b83fc02b --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1500255.js @@ -0,0 +1,11 @@ + +setJitCompilerOption("offthread-compilation.enable", 0); +setJitCompilerOption("baseline.warmup.trigger", 0); +setJitCompilerOption("ion.warmup.trigger", 0); + +foo(); + +function foo() { + Array.prototype.__proto__ = null; + Array.prototype[1] = 'bar'; +} diff --git a/js/src/jit-test/tests/cacheir/bug1502143.js b/js/src/jit-test/tests/cacheir/bug1502143.js new file mode 100644 index 0000000000..2cc7bc1f55 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1502143.js @@ -0,0 +1,15 @@ + +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 100); + +main(); + +function main() { + new Uint8Array(1024).buffer; + var b = []; + b[10000] = 1; + [].concat(b) + var a2 = new Int32Array(2); + b.__proto__ = a2; + [].concat(b) +} diff --git a/js/src/jit-test/tests/cacheir/bug1502709.js b/js/src/jit-test/tests/cacheir/bug1502709.js new file mode 100644 index 0000000000..535443050d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1502709.js @@ -0,0 +1,17 @@ + +setJitCompilerOption("baseline.warmup.trigger", 0); +main(); + +function main() { + try { + x = []; + Object.defineProperty(x, 1, {}); + y = []; + Object.defineProperty(y, 1, {}); + y.__proto__ = null; + Array.prototype.sort.apply(x, [function() {}]); + } catch (e) {} + try { + Array.prototype.sort.apply(y, [function() {}]); + } catch (e) {} +} diff --git a/js/src/jit-test/tests/cacheir/bug1509293.js b/js/src/jit-test/tests/cacheir/bug1509293.js new file mode 100644 index 0000000000..34306bc223 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1509293.js @@ -0,0 +1,2 @@ +var summary = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; +Array.prototype.push([...summary]); diff --git a/js/src/jit-test/tests/cacheir/bug1514682.js b/js/src/jit-test/tests/cacheir/bug1514682.js new file mode 100644 index 0000000000..c509700b4e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1514682.js @@ -0,0 +1,15 @@ +function f(o, n) { + if (n) { + o[n] = true; + } else { + o.x = true; + } +} + +// Warm up object so HadElementsAccess check will trip next +var o = {}; +for (var i = 0; i < 43; ++i) { + o["x" + i] = true; +} + +f(o, "y"); diff --git a/js/src/jit-test/tests/cacheir/bug1526872.js b/js/src/jit-test/tests/cacheir/bug1526872.js new file mode 100644 index 0000000000..33d65e9443 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1526872.js @@ -0,0 +1,12 @@ + +setJitCompilerOption("offthread-compilation.enable", 0); +setJitCompilerOption("baseline.warmup.trigger", 0); +setJitCompilerOption("ion.warmup.trigger", 0); + +for (var i = 0; i < 5; i++) { + assertEq(foo(1n), false); +} + +function foo(x) { + return x == null || x == undefined; +} diff --git a/js/src/jit-test/tests/cacheir/bug1536228.js b/js/src/jit-test/tests/cacheir/bug1536228.js new file mode 100644 index 0000000000..2a094eb377 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1536228.js @@ -0,0 +1,40 @@ +load(libdir + "asserts.js"); + +function test() { + var plainDataProperty = { + value: 0, enumerable: true, configurable: true, writable: true + }; + var nonEnumerableProperty = { + value: 0, enumerable: false, configurable: true, writable: true + }; + var nonConfigurableProperty = { + value: 0, enumerable: true, configurable: false, writable: true + }; + var n = 5000; + for (var i = 0; i < n; ++i) { + var obj = {}; + + // Create a different shape for each object to ensure JSOP_INITELEM + // below can get into the megamorphic state. + Object.defineProperty(obj, "bar" + i, nonEnumerableProperty); + + // Plain data property, will be created through DefineDataProperty, + // which is emitted as JSOP_INITELEM. The object doesn't have a "foo" + // property, so JSOP_INITELEM can simply create a new property. + Object.defineProperty(obj, "foo", plainDataProperty); + + // Redefine as a non-data property for the last object. + var desc = (i + 1 !== n) ? plainDataProperty : nonConfigurableProperty; + Object.defineProperty(obj, "foo", desc); + + // Again JSOP_INITELEM, but this time JSOP_INITELEM can't simply add a + // property, because "foo" is already present. And for the last object, + // which has a non-configurable "foo" property, this call must throw a + // TypeError exception. + Object.defineProperty(obj, "foo", plainDataProperty); + } +} + +for (var i = 0; i < 2; ++i) { + assertThrowsInstanceOf(test, TypeError); +} diff --git a/js/src/jit-test/tests/cacheir/bug1612636.js b/js/src/jit-test/tests/cacheir/bug1612636.js new file mode 100644 index 0000000000..710889b308 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1612636.js @@ -0,0 +1,10 @@ +var concat = String.prototype.concat; +function f18(b83) { + var a11 = arguments; + if (b83) + f18(false); + g22 = {}; + g22.apply = concat; + g22.apply(null, a11); +} +f18(true); diff --git a/js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js b/js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js new file mode 100644 index 0000000000..a64e2aabb0 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js @@ -0,0 +1,28 @@ +class Base { + static a = 0; + static [Symbol.iterator] = 0; +} + +class Derived extends Base { + static m(key) { + // Attach an IC through IonGetPropSuperIC. + return super[key]; + } +} + +var key = { + value: "a", + + [Symbol.toPrimitive]() { + return this.value; + } +}; + +for (var i = 0; i < 100; ++i) { + // Change key[Symbol.toPrimitive] to return a symbol after some warm-up. + if (i > 80) { + key.value = Symbol.iterator; + } + + assertEq(Derived.m(key), 0); +} diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js new file mode 100644 index 0000000000..9a4362f410 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js @@ -0,0 +1,23 @@ +// An object with a custom [Symbol.toPrimitive] function. +var key = { + value: "a", + + [Symbol.toPrimitive]() { + return this.value; + } +}; + +var target = {}; +var obj = new Proxy(target, {}); + +for (var i = 0; i < 100; ++i) { + // Change key[Symbol.toPrimitive] to return a symbol after some warm-up. + if (i > 80) { + key.value = Symbol.iterator; + } + + target[key.value] = i; + + // Attach an IC for JSOp::GetElem on proxies. + assertEq(obj[key], i); +} diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js new file mode 100644 index 0000000000..004b5982d8 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js @@ -0,0 +1,24 @@ +// An object with a custom [Symbol.toPrimitive] function. +var key = { + value: "a", + + [Symbol.toPrimitive]() { + return this.value; + } +}; + +var target = { + a: 0, + [Symbol.iterator]: 0, +}; +var obj = new Proxy(target, {}); + +for (var i = 0; i < 100; ++i) { + // Change key[Symbol.toPrimitive] to return a symbol after some warm-up. + if (i > 80) { + key.value = Symbol.iterator; + } + + // Attach an IC for JSOp::In on proxies. + assertEq(key in obj, true); +} diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js new file mode 100644 index 0000000000..90f94a4e48 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js @@ -0,0 +1,24 @@ +// An object with a custom [Symbol.toPrimitive] function. +var key = { + value: "a", + + [Symbol.toPrimitive]() { + return this.value; + } +}; + +var target = { + a: 0, + [Symbol.iterator]: 0, +}; +var obj = new Proxy(target, {}); + +for (var i = 0; i < 100; ++i) { + // Change key[Symbol.toPrimitive] to return a symbol after some warm-up. + if (i > 80) { + key.value = Symbol.iterator; + } + + // Attach an IC for JSOp::HasOwn on proxies. + assertEq(Object.prototype.hasOwnProperty.call(obj, key), true); +} diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js new file mode 100644 index 0000000000..a52362a91f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js @@ -0,0 +1,23 @@ +// An object with a custom [Symbol.toPrimitive] function. +var key = { + value: "a", + + [Symbol.toPrimitive]() { + return this.value; + } +}; + +var target = {}; +var obj = new Proxy(target, {}); + +for (var i = 0; i < 100; ++i) { + // Change key[Symbol.toPrimitive] to return a symbol after some warm-up. + if (i > 80) { + key.value = Symbol.iterator; + } + + obj[key] = i; + + // Attach an IC for JSOp::SetElem on proxies. + assertEq(target[key.value], i); +} diff --git a/js/src/jit-test/tests/cacheir/bug1654947.js b/js/src/jit-test/tests/cacheir/bug1654947.js new file mode 100644 index 0000000000..673e5fd0c2 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1654947.js @@ -0,0 +1,11 @@ +function f() { + let g = x => (x >>> {} >>> x, x); + let arr = [0, 0xffff, undefined]; + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 3; j++) { + let y = g(arr[j]); + assertEq(y, arr[j]); + } + } +} +f(); diff --git a/js/src/jit-test/tests/cacheir/bug1685684.js b/js/src/jit-test/tests/cacheir/bug1685684.js new file mode 100644 index 0000000000..06b37a852c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1685684.js @@ -0,0 +1,8 @@ +let g = newGlobal({immutablePrototype: false}); +g.__proto__ = {}; +g.evaluate(`(function() { + for (var i = 0; i < 5; i++) { + d = eval('1'); + assertEq(d, 1); + } +})()`); diff --git a/js/src/jit-test/tests/cacheir/bug1685925-1.js b/js/src/jit-test/tests/cacheir/bug1685925-1.js new file mode 100644 index 0000000000..630b667026 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1685925-1.js @@ -0,0 +1,15 @@ +// |jit-test| error: TypeError: get length method +function f(o) { + return o.length; +} +let objects = [ + {}, + {length: 0}, + [], + {x: 0, length: 0}, + {x: 0, y: 0, length: 0}, + {x: 0, y: 0, z: 0, length: 0}, + new Uint32Array(), + Object.create(new Uint8Array()), +]; +objects.forEach(f); diff --git a/js/src/jit-test/tests/cacheir/bug1685925-2.js b/js/src/jit-test/tests/cacheir/bug1685925-2.js new file mode 100644 index 0000000000..c009a1c0ec --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1685925-2.js @@ -0,0 +1,7 @@ +// |jit-test| error: TypeError: get length method +setJitCompilerOption("ic.force-megamorphic", 1); +function f(o) { + return o.length; +} +f(new Int8Array(4)); +f(Object.create(new Uint8Array())); diff --git a/js/src/jit-test/tests/cacheir/bug1713556.js b/js/src/jit-test/tests/cacheir/bug1713556.js new file mode 100644 index 0000000000..0db7a3aa25 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1713556.js @@ -0,0 +1,25 @@ +// |jit-test| skip-if: !wasmIsSupported() + +function wasmEvalText(str ) { + binary = wasmTextToBinary(str) + m = new WebAssembly.Module(binary) + return new WebAssembly.Instance(m) +} +ins = wasmEvalText(` + (func (export "get1") (param)) +`); +ins.exports.get1( + 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 + ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 +) diff --git a/js/src/jit-test/tests/cacheir/bug1757634.js b/js/src/jit-test/tests/cacheir/bug1757634.js new file mode 100644 index 0000000000..97ea3a87f4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1757634.js @@ -0,0 +1,29 @@ +function testSmallIndex() { + var proto = Object.create(null); + var arr = []; + Object.setPrototypeOf(arr, proto); + + proto[0] = 123; + Object.freeze(proto); + + for (var i = 0; i < 20; i++) { + arr[0] = 321; + } + assertEq(arr[0], 123); +} +testSmallIndex(); + +function testLargeIndex() { + var proto = Object.create(null); + var arr = []; + Object.setPrototypeOf(arr, proto); + + proto[98765432] = 123; + Object.freeze(proto); + + for (var i = 0; i < 20; i++) { + arr[98765432] = 321; + } + assertEq(arr[98765432], 123); +} +testLargeIndex(); diff --git a/js/src/jit-test/tests/cacheir/bug1772824.js b/js/src/jit-test/tests/cacheir/bug1772824.js new file mode 100644 index 0000000000..f399a77cea --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1772824.js @@ -0,0 +1,15 @@ +function foo() { eval(); boundName += 1; } + +boundName = 0; +for (var i = 0; i < 10; i++) { + eval("var x" + i + " = 0;"); + foo(); +} + +// Redefine variable as const +evaluate(` + const boundName = 0; + for (var i = 0; i < 2; i++) { + try { foo(); } catch {} + } +`); diff --git a/js/src/jit-test/tests/cacheir/bug1785200.js b/js/src/jit-test/tests/cacheir/bug1785200.js new file mode 100644 index 0000000000..d568f6108d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1785200.js @@ -0,0 +1,15 @@ +const target = {}; +const receiver = {}; +const id = 5; + +let count = 0; +function getter() { + count++; + assertEq(this, receiver); +} +Object.defineProperty(target, id, { get: getter }); + +for (var i = 0; i < 100; i++) { + Reflect.get(target, id, receiver); +} +assertEq(count, 100); diff --git a/js/src/jit-test/tests/cacheir/bug1788528-1.js b/js/src/jit-test/tests/cacheir/bug1788528-1.js new file mode 100644 index 0000000000..038ef1e12e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1788528-1.js @@ -0,0 +1,15 @@ +// |jit-test| error:ReferenceError: can't access lexical declaration +function f(i) { + if (i === 19) { + g(); + } + let val = 0; + function g() { + eval(""); + val ||= 1; + } + g(); +} +for (var i = 0; i < 20; i++) { + f(i); +} diff --git a/js/src/jit-test/tests/cacheir/bug1788528-2.js b/js/src/jit-test/tests/cacheir/bug1788528-2.js new file mode 100644 index 0000000000..5f3701c3b0 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1788528-2.js @@ -0,0 +1,15 @@ +// |jit-test| error:ReferenceError: can't access lexical declaration +function f(i) { + if (i === 19) { + g(); + } + let val = 0; + function g() { + eval(""); + val = 1; + } + g(); +} +for (var i = 0; i < 20; i++) { + f(i); +} diff --git a/js/src/jit-test/tests/cacheir/bug1788528-3.js b/js/src/jit-test/tests/cacheir/bug1788528-3.js new file mode 100644 index 0000000000..52414669d4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1788528-3.js @@ -0,0 +1,15 @@ +// |jit-test| error:ReferenceError: can't access lexical declaration +function f(i) { + if (i === 19) { + g(); + } + let val = 0; + function g() { + eval(""); + val += 1; + } + g(); +} +for (var i = 0; i < 20; i++) { + f(i); +} diff --git a/js/src/jit-test/tests/cacheir/bug1788528-4.js b/js/src/jit-test/tests/cacheir/bug1788528-4.js new file mode 100644 index 0000000000..67ab57e433 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1788528-4.js @@ -0,0 +1,15 @@ +// |jit-test| error:ReferenceError: can't access lexical declaration +function f(i) { + if (i === 19) { + g(); + } + let val = 0; + function g() { + eval(""); + val; + } + g(); +} +for (var i = 0; i < 20; i++) { + f(i); +} diff --git a/js/src/jit-test/tests/cacheir/bug1804634.js b/js/src/jit-test/tests/cacheir/bug1804634.js new file mode 100644 index 0000000000..c34e815f6b --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1804634.js @@ -0,0 +1,34 @@ +// |jit-test| --ion-instruction-reordering=off + +function inner(o) { + return o.x; +} +function outer(o) { + return inner(o); +} + +with ({}) {} + +var arr = []; +for (var i = 0; i < 3; i++) { + var obj = {x: 1}; + obj["y" + i] = 2; + arr.push(obj); +} + +// Transpile a GuardMultipleShapes with 3 shapes. +for (var i = 0; i < 2000; i++) { + assertEq(outer(arr[i % arr.length]), 1); +} + +// Bail out ten times with new shapes. +for (var i = 0; i < 10; i++) { + var obj = {x: 1}; + obj["z" + i] = 3; + assertEq(outer(obj), 1); +} + +// Trigger a recompilation. +for (var i = 0; i < 2000; i++) { + assertEq(outer(arr[i % arr.length]), 1); +} diff --git a/js/src/jit-test/tests/cacheir/bug1819486.js b/js/src/jit-test/tests/cacheir/bug1819486.js new file mode 100644 index 0000000000..6bb3e799ea --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1819486.js @@ -0,0 +1,18 @@ +class Base {} +class Derived extends Base { + constructor(a) { super(a); this.a = a; } +} +function test() { + var boundCtor = Derived.bind(); + for (var i = 0; i < 40; i++) { + new boundCtor(); + var ex = null; + try { + boundCtor(); + } catch(e) { + ex = e; + } + assertEq(ex instanceof TypeError, true); + } +} +test(); diff --git a/js/src/jit-test/tests/cacheir/bug1823212.js b/js/src/jit-test/tests/cacheir/bug1823212.js new file mode 100644 index 0000000000..951b3794cc --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1823212.js @@ -0,0 +1,13 @@ +with (newGlobal(this)) { + eval(` + function bar() {} + function foo(a) { + try { + foo(); + } catch { + bar(...arguments); + } + } + foo(); + `); +} diff --git a/js/src/jit-test/tests/cacheir/bug1834038.js b/js/src/jit-test/tests/cacheir/bug1834038.js new file mode 100644 index 0000000000..fb7581d3fc --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1834038.js @@ -0,0 +1,9 @@ +function foo() { + const v4 = []; + v4[1024] = {}; + v4.splice(0).push(0); +} + +for (let v0 = 0; v0 < 1000; v0++) { + foo(); +} diff --git a/js/src/jit-test/tests/cacheir/bug1837157.js b/js/src/jit-test/tests/cacheir/bug1837157.js new file mode 100644 index 0000000000..c468a1a259 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1837157.js @@ -0,0 +1,36 @@ +// |jit-test| --fast-warmup; --no-threads +with ({}) {} + +function foo(a) { + a.prop = 0; +} + +class A { + set prop(x) { } +} + +function newShape() { + class B extends A {} + return new B(); +} + +function triggerIonCompile() { + with ({}) {} + for (var i = 0; i < 50; i++) { + foo(initialShapes[i % initialShapes.length]) + } +} + +var initialShapes = []; +for (var i = 0; i < 8; i++) { + initialShapes.push(newShape()); +} + +triggerIonCompile(); + +for (var i = 0; i < 10; i++) { + foo(0); +} +foo(newShape()); + +triggerIonCompile(); diff --git a/js/src/jit-test/tests/cacheir/bug1842617.js b/js/src/jit-test/tests/cacheir/bug1842617.js new file mode 100644 index 0000000000..1524d5733f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1842617.js @@ -0,0 +1,30 @@ +// |jit-test| --baseline-eager + +function foo(src) { + try { + evaluate(src); + } catch {} + Math = undefined; +} + +foo(``); +foo(``); +foo(`var a = 0;`); +foo(`var b = 0;`); +foo(`var c = 0;`); +foo(`var d = 0;`); +foo(`undef();`); +foo(`{`); +foo(` + gc(); + e = 0; + gc(); +`); +foo(` + var f = 0; + gc(); + delete Math; +`); +foo(` + g = undefined; +`); diff --git a/js/src/jit-test/tests/cacheir/bug1851599.js b/js/src/jit-test/tests/cacheir/bug1851599.js new file mode 100644 index 0000000000..c960ee3f10 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1851599.js @@ -0,0 +1,44 @@ +// |jit-test| --fast-warmup; --no-threads + +function foo(phase, o1, o2) { + switch (phase) { + case 1: + return o1.x; + case 2: + return o1.x + o2.x; + } +} + +// Set `foo` as last child and `bar` as last parent. +function phase1() { + eval(` + function bar(o) { + foo(1, o); + } + with ({}) {} + for (var j = 0; j < 100; j++) { + var obj = {x: 1}; + obj["y" + (j % 10)] = 2; + bar(obj); + } + bar({y: 1, x: 2}); + `); +} +phase1(); + +// Collect `bar`. +gc(); + +// Recompile `foo` monomorphically. +with ({}) {} +for (var i = 0; i < 100; i++) { + foo(2, {x: 1}, {x: 1}); +} + +// Bail out and create a folded stub in `foo`. +// The child matches, so we use `bar` as the owning script. +for (var i = 0; i < 6; i++) { + var obj = {x: 1}; + obj["y" + i] = 2; + foo(2, {y: 1, x: 2}, obj); +} diff --git a/js/src/jit-test/tests/cacheir/bug1851911.js b/js/src/jit-test/tests/cacheir/bug1851911.js new file mode 100644 index 0000000000..c066ed9c3e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1851911.js @@ -0,0 +1,35 @@ +// |jit-test| --fast-warmup; --no-threads; --blinterp-eager + +function foo(o) { + return o.x; +} + +function bar(n) { + with ({}) {} + class C {} + for (var i = 0; i < n; i++) { + var c = new C(); + c.x = 0; + foo(c); + } +} + +with ({}) {} + +function triggerIonCompile() { + for (var i = 0; i < 10; i++) { + bar(3); + } +} + +triggerIonCompile(); + +// Fill up shape list +for (var i = 0; i < 6; i++) { + bar(10); +} + +bar(10); +gc(); + +triggerIonCompile(); diff --git a/js/src/jit-test/tests/cacheir/bug1852893-1.js b/js/src/jit-test/tests/cacheir/bug1852893-1.js new file mode 100644 index 0000000000..150c6252ae --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1852893-1.js @@ -0,0 +1,27 @@ +// |jit-test| --fast-warmup; --no-threads; --blinterp-eager + +function foo(o) { + return foo_inner(o); +} + +function foo_inner(o) { + return o.x; +} +with ({}) {} + +for (var i = 0; i < 13; i++) { + foo({x:1, ["y" + (i % 5)]: 2}); +} + +for (var i = 0; i < 17; i++) { + foo({x: 1, ["y" + (i % 8)]: 2}); +} + +eval("for (var i = 0; i < 10; i++) foo({y: 1, x:2})") + +// Purge a stub inside a trial-inlined script. +gc(); + +for (var i = 0; i < 50; i++) { + foo({x: 1, ["y" + (i % 3)]: 2}); +} diff --git a/js/src/jit-test/tests/cacheir/bug1852893-2.js b/js/src/jit-test/tests/cacheir/bug1852893-2.js new file mode 100644 index 0000000000..daf1d2d8b9 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1852893-2.js @@ -0,0 +1,42 @@ +// |jit-test| --fast-warmup; --no-threads; --blinterp-eager + +function foo(o) { + return foo_inner(o); +} + +function foo_inner(o) { + return o.x; +} + +function bar(n) { + with ({}) {} + class C {} + for (var i = 0; i < n; i++) { + var c = new C(); + c.x = 0; + foo(c); + } +} + +with ({}) {} + +function triggerIonCompile() { + for (var i = 0; i < 10; i++) { + bar(3); + } +} + +triggerIonCompile(); + +// Fill up shape list +for (var i = 0; i < 6; i++) { + bar(10); +} + +// Overflow shape list, adding a new baseline IC. +bar(10); + +// Purge a stub inside a monomorphically-inlined script. +gc(); + +triggerIonCompile(); diff --git a/js/src/jit-test/tests/cacheir/call-any-native.js b/js/src/jit-test/tests/cacheir/call-any-native.js new file mode 100644 index 0000000000..7af6d2102c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/call-any-native.js @@ -0,0 +1,59 @@ +// Test non-constructor calls + +var funcs = [Math.max, Math.min, Math.floor, Math.ceil, Math.sin, + Math.cos, Math.tan, Math.log, Math.acos, Math.asin]; + +// Calculate expected values +var expected = [Math.max(0.5, 2), Math.min(0.5, 2), + Math.floor(0.5, 2), Math.ceil(0.5, 2), + Math.sin(0.5, 2), Math.cos(0.5, 2), + Math.tan(0.5, 2), Math.log(0.5, 2), + Math.acos(0.5, 2), Math.asin(0.5, 2)]; + +// Test a polymorphic call site +for (var n = 0; n < 50; n++) { + for (var i = 0; i < funcs.length; i++) { + assertEq(funcs[i](0.5, 2), expected[i]); + } +} + +// Test a polymorphic spread call site +var spreadinput = [0.5, 2]; +for (var n = 0; n < 50; n++) { + for (var i = 0; i < funcs.length; i++) { + assertEq(funcs[i](...spreadinput), expected[i]); + } +} + +// Test constructors + +function f1(x) {this[0] = x; this.length = 3;} +function f2(x) {this[0] = x; this.length = 3;} +function f3(x) {this[0] = x; this.length = 3;} +function f4(x) {this[0] = x; this.length = 3;} +function f5(x) {this[0] = x; this.length = 3;} +function f6(x) {this[0] = x; this.length = 3;} +function f7(x) {this[0] = x; this.length = 3;} +function f8(x) {this[0] = x; this.length = 3;} +function f9(x) {this[0] = x; this.length = 3;} + +var constructors = [f1,f2,f3,f4,f5,f6,f7,f8,f9,Array]; + +// Test a polymorphic constructor site +for (var n = 0; n < 50; n++) { + for (var i = 0; i < constructors.length; i++) { + let x = new constructors[i](1,2,3); + assertEq(x.length, 3); + assertEq(x[0], 1); + } +} + +var constructorinput = [1,2,3]; +// Test a polymorphic spread constructor site +for (var n = 0; n < 50; n++) { + for (var i = 0; i < constructors.length; i++) { + let x = new constructors[i](...constructorinput); + assertEq(x.length, 3); + assertEq(x[0], 1); + } +} diff --git a/js/src/jit-test/tests/cacheir/call-bound-function-many-args.js b/js/src/jit-test/tests/cacheir/call-bound-function-many-args.js new file mode 100644 index 0000000000..1e17aa70e1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/call-bound-function-many-args.js @@ -0,0 +1,63 @@ +function sum(...args) { + var res = 0; + for (var i = 0; i < args.length; i++) { + res += args[i]; + } + return {v: res}; +} + +function testManyArgs1() { + var bound = sum.bind(null); + for (var i = 0; i < 20; i++) { + assertEq(bound(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255).v, + 32640); + assertEq(new bound(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255).v, + 32640); + } +} +testManyArgs1(); + +function testManyArgs2() { + var bound = sum.bind(null, 1, 2, 3, 4, 5, 6); + for (var i = 0; i < 20; i++) { + assertEq(bound(7, 8, 9, 10, 11, 12).v, 78); + assertEq(bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20).v, 210); + assertEq(bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29).v, 435); + assertEq(new bound(7, 8, 9, 10, 11, 12).v, 78); + assertEq(new bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20).v, 210); + assertEq(new bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29).v, 435); + } +} +testManyArgs2(); diff --git a/js/src/jit-test/tests/cacheir/call-bound-scripted.js b/js/src/jit-test/tests/cacheir/call-bound-scripted.js new file mode 100644 index 0000000000..960ca1f8ac --- /dev/null +++ b/js/src/jit-test/tests/cacheir/call-bound-scripted.js @@ -0,0 +1,84 @@ +function testNumBoundArgsGuard() { + function f(a, b, c, d, e, f, g, h) { + return [a, b, c, d, e, f, g, h].join(","); + } + var arr = [ + f.bind(null, 1, 2, 3, 4, 5, 6), + f.bind(null, 4, 5, 6, 7, 8, 9), + f.bind(null, 1, 2), + f.bind(null, 1), + ]; + var expected = [ + "1,2,3,4,5,6,10,11", + "4,5,6,7,8,9,10,11", + "1,2,10,11,12,13,14,15", + "1,10,11,12,13,14,15,", + ]; + for (var i = 0; i < 100; i++) { + assertEq(arr[0](10, 11, 12, 13, 14, 15), expected[0]); + assertEq(arr[1](10, 11, 12, 13, 14, 15), expected[1]); + assertEq(arr[2](10, 11, 12, 13, 14, 15), expected[2]); + assertEq(arr[3](10, 11, 12, 13, 14, 15), expected[3]); + assertEq(arr[i % arr.length](10, 11, 12, 13, 14, 15), expected[i % arr.length]); + } +} +testNumBoundArgsGuard(); + +function testJSFunctionGuard() { + var arr = [ + (x => x * 0).bind(null, 1), + (x => x * 1).bind(null, 2), + (x => x * 2).bind(null, 3), + (x => x * 3).bind(null, 4), + (x => x * 4).bind(null, 5), + (x => x * 5).bind(null, 6), + (x => x * 6).bind(null, 7), + (x => x * 7).bind(null, 8), + (x => x * 8).bind(null, 9), + ]; + var boundNative = Math.abs.bind(null, -123); + var boundProxy = (new Proxy(x => x * 42, {})).bind(null, 7); + + for (var i = 0; i < 100; i++) { + var idx = i % arr.length; + var fun1 = arr[idx]; + var expected1 = idx * (idx + 1); + assertEq(fun1(), expected1); + var fun2 = i > 70 ? boundNative : fun1; + assertEq(fun2(), i > 70 ? 123 : expected1); + var fun3 = i > 70 ? boundProxy : fun1; + assertEq(fun3(), i > 70 ? 294 : expected1); + } +} +testJSFunctionGuard(); + +function testCrossRealmTarget() { + globalThis.num = "FAIL"; + + var globals = []; + for (var i = 0; i < 4; i++) { + var g = newGlobal({sameCompartmentAs: this}); + g.num = i; + globals.push(g); + } + var arr = globals.map(g => g.evaluate("(x => this.num)")); + arr = arr.map(f => Function.prototype.bind.call(f, null)); + + for (var i = 0; i < 200; i++) { + assertEq(arr[0](), 0); + var idx = i % arr.length; + assertEq(arr[idx](), idx); + } +} +testCrossRealmTarget(); + +// Bug 1819651. +function testReturnsItself() { + var fun = function() { return boundFun; }; + var boundFun = fun.bind(null); + for (var i = 0; i < 20; i++) { + assertEq(boundFun(), boundFun); + assertEq(new boundFun(), boundFun); + } +} +testReturnsItself(); diff --git a/js/src/jit-test/tests/cacheir/call-native-get-element-super.js b/js/src/jit-test/tests/cacheir/call-native-get-element-super.js new file mode 100644 index 0000000000..c19baab62c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/call-native-get-element-super.js @@ -0,0 +1,44 @@ +// |jit-test| --fast-warmup; --no-threads + +const target = {}; +const receiver = {}; + +let count = 0; +function getter() { + count++; + assertEq(this, receiver); +} + +var x = Math.hypot(1,0); +var y = 2**31 - 1; +var z = -1; + +Object.defineProperty(target, x, { get: getter }); +Object.defineProperty(target, y, { get: getter }); +Object.defineProperty(target, z, { get: getter }); + +function get(id) { + return Reflect.get(target, id, receiver); +} + +function test() { + with ({}) {} + count = 0; + for (var i = 0; i < 100; i++) { + get(x); + get(y); + get(z); + } + assertEq(count, 300); +} + +// Test baseline IC / transpiled to MIR +test(); + +// Force an IC in Ion. +for (var i = 0; i < 10; i++) { + get("a"); +} + +// Test ion IC +test(); diff --git a/js/src/jit-test/tests/cacheir/ccw-missing.js b/js/src/jit-test/tests/cacheir/ccw-missing.js new file mode 100644 index 0000000000..275b998558 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/ccw-missing.js @@ -0,0 +1,4 @@ +var wrapper = evaluate("var o = {}; o", {global: newGlobal({sameZoneAs: this})}); +for (var i = 0; i < 50; i++) { + assertEq(wrapper.x, undefined); +} diff --git a/js/src/jit-test/tests/cacheir/compare-null-or-undef.js b/js/src/jit-test/tests/cacheir/compare-null-or-undef.js new file mode 100644 index 0000000000..b39eefd957 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/compare-null-or-undef.js @@ -0,0 +1,93 @@ +// Test relational comparison when one operand is null or undefined. + +function test(xs) { + for (let i = 0; i < 200; ++i) { + let x = xs[i % xs.length]; + + // The result is equal when compared to the result with explicit ToNumber conversions. + + // Test when null-or-undefined is on the right-hand side. + assertEq(x < nullOrUndef, (+x) < (+nullOrUndef)); + assertEq(x <= nullOrUndef, (+x) <= (+nullOrUndef)); + assertEq(x >= nullOrUndef, (+x) >= (+nullOrUndef)); + assertEq(x > nullOrUndef, (+x) > (+nullOrUndef)); + + // Test when null-or-undefined is on the left-hand side. + assertEq(nullOrUndef < x, (+nullOrUndef) < (+x)); + assertEq(nullOrUndef <= x, (+nullOrUndef) <= (+x)); + assertEq(nullOrUndef >= x, (+nullOrUndef) >= (+x)); + assertEq(nullOrUndef > x, (+nullOrUndef) > (+x)); + } +} + +function runTest(inputs) { + let fNull = Function(`return ${test}`.replaceAll("nullOrUndef", "null"))(); + fNull(inputs); + + let fUndefined = Function(`return ${test}`.replaceAll("nullOrUndef", "undefined"))(); + fUndefined(inputs); +} + +// Null inputs. +runTest([ + null, +]); + +// Undefined inputs. +runTest([ + undefined, +]); + +// Null or undefined inputs. +runTest([ + null, + undefined, +]); + +// Int32 inputs +runTest([ + -0x8000_0000, + -0x7fff_ffff, + -0x7fff_fffe, + -2, + -1, + 0, + 1, + 2, + 0x7fff_fffe, + 0x7fff_ffff, +]); + +// Number inputs +runTest([ + Number.NEGATIVE_INFINITY, + -Number.MAX_VALUE, + Number.MIN_SAFE_INTEGER, + -1.5, + -0.5, + -Number.EPSILON, + -Number.MIN_VALUE, + -0, + 0, + Number.MIN_VALUE, + Number.EPSILON, + 0.5, + 1.5, + Number.MAX_SAFE_INTEGER, + Number.MAX_VALUE, + Number.POSITIVE_INFINITY, + NaN, +]); + +// Boolean inputs. +runTest([ + true, false, +]); + +// String inputs +runTest([ + "", + "0", "-0", "0.0", "0e100", + "1", "1.5", "-1234", "-1e0", + "Infinity", "NaN", "not-a-number", +]); diff --git a/js/src/jit-test/tests/cacheir/compare.js b/js/src/jit-test/tests/cacheir/compare.js new file mode 100644 index 0000000000..3656a7d0ee --- /dev/null +++ b/js/src/jit-test/tests/cacheir/compare.js @@ -0,0 +1,292 @@ +setJitCompilerOption('ion.forceinlineCaches', 1); + +function warmup(fun, input_array) { + for (var index = 0; index < input_array.length; index++) { + input = input_array[index]; + input_lhs = input[0]; + input_rhs = input[1]; + output = input[2]; + for (var i = 0; i < 30; i++) { + var str = ""; + var y = fun(input_lhs, input_rhs); + if (y != output) { + str = "Computed: "+y+ ", expected: "+ output + " ( " + fun + " lhs: "+ input_lhs + " rhs: " + input_rhs +")"; + } + assertEq(str, ""); + } + } +} + + +// Int32 + Int32 +var Int32Int32Fun_GT1 = (a, b) => { return a > b; } +warmup(Int32Int32Fun_GT1, [[1,2, false], [1,1, false], [3,4, false], [4294967295, 2, true], + [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]); + +var Int32Int32Fun_GTE1 = (a, b) => { return a >= b; } +warmup(Int32Int32Fun_GTE1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, true], + [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]); + +var Int32Int32Fun_LT1 = (a, b) => { return a < b; } +warmup(Int32Int32Fun_LT1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, false], + [NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]); + +var Int32Int32Fun_LTE1 = (a, b) => { return a <= b; } +warmup(Int32Int32Fun_LTE1, [[1,2, true], [1,1, true], [3,4, true], [4294967295, 2, false], + [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]); + +var Int32Int32Fun_EQ1 = (a, b) => { return a == b; } +warmup(Int32Int32Fun_EQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false], + [NaN, 2, false], [-1000, NaN, false], [undefined, null, true], + ['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]); + +var Int32Int32Fun_EQ2 = (a, b) => { return a == b; } +warmup(Int32Int32Fun_EQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]); + +var Int32Int32Fun_EQ3 = (a, b) => { return a == b; } +warmup(Int32Int32Fun_EQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]); + +var Int32Int32Fun_EQ4 = (a, b) => { return a == b; } +warmup(Int32Int32Fun_EQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, true], [undefined, null, true]]); + + +var Int32Int32Fun_NEQ1 = (a, b) => { return a != b; } +warmup(Int32Int32Fun_NEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true], + [NaN, 2, true], [-1000, NaN, true], [undefined, null, false], + ['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]); + +var Int32Int32Fun_NEQ2 = (a, b) => { return a != b; } +warmup(Int32Int32Fun_NEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]); + +var Int32Int32Fun_NEQ3 = (a, b) => { return a != b; } +warmup(Int32Int32Fun_NEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]); + +var Int32Int32Fun_NEQ4 = (a, b) => { return a != b; } +warmup(Int32Int32Fun_NEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, false], [undefined, null, false]]); + +var Int32Int32Fun_SEQ1 = (a, b) => { return a === b; } +warmup(Int32Int32Fun_SEQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false], + [NaN, 2, false], [-1000, NaN, false], [undefined, null, false], + ['0', 0, false], [new String('0'), 0, false], [10, undefined, false]]); + +var Int32Int32Fun_SEQ2 = (a, b) => { return a === b; } +warmup(Int32Int32Fun_SEQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]); + +var Int32Int32Fun_SEQ3 = (a, b) => { return a === b; } +warmup(Int32Int32Fun_SEQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]); + +var Int32Int32Fun_SEQ4 = (a, b) => { return a === b; } +warmup(Int32Int32Fun_SEQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, false], [undefined, null, false] ]); + +var Int32Int32Fun_SEQ5 = (a, b) => { return a === b; } +warmup(Int32Int32Fun_SEQ5, [[1, true, false], [1, false, false], [false, 1, false], [true, 0, false], [true, true, true]]); + +var Int32Int32Fun_SNEQ1 = (a, b) => { return a !== b; } +warmup(Int32Int32Fun_SNEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true], + [NaN, 2, true], [-1000, NaN, true], [undefined, null, true], + ['0', 0, true], [new String('0'), 0, true], [10, undefined, true]]); + +var Int32Int32Fun_SNEQ2 = (a, b) => { return a !== b; } +warmup(Int32Int32Fun_SNEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]); + +var Int32Int32Fun_SNEQ3 = (a, b) => { return a !== b; } +warmup(Int32Int32Fun_SNEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]); + +var Int32Int32Fun_SNEQ4 = (a, b) => { return a !== b; } +warmup(Int32Int32Fun_SNEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, true], [undefined, null, true] ]); + +var Int32Int32Fun_SNEQ5 = (a, b) => { return a !== b; } +warmup(Int32Int32Fun_SNEQ5, [[1, true, true], [1, false, true], [false, 1, true], [true, 0, true]]); + + +// Number Number +var NumberNumberFun_GT1 = (a, b) => { return a > b; } +warmup(NumberNumberFun_GT1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false], + [1,1, false], [3,4, false], [4294967295, 2, true], + [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]); + +var NumberNumberFun_GTE1 = (a, b) => { return a >= b; } +warmup(NumberNumberFun_GTE1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false], + [1,1, true], [3,4, false], [4294967295, 2, true], + [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]); + +var NumberNumberFun_LT1 = (a, b) => { return a < b; } +warmup(NumberNumberFun_LT1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true], + [1,1, false], [3,4, true], [4294967295, 2, false], + [NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false, [10.2, undefined, false]]]); + +var NumberNumberFun_LTE1 = (a, b) => { return a <= b; } +warmup(NumberNumberFun_LTE1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true], + [1,1, true], [3,4, true], [4294967295, 2, false], + [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]); + +var NumberNumberFun_EQ1 = (a, b) => { return a == b; } +warmup(NumberNumberFun_EQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false], + [1,1, true], [3,4, false], [4294967295, 2, false], + [NaN, 2, false], [-1000, NaN, false], [undefined, null, true], + ['0', 0, true], [new String('0'), 0, true], [10.2, undefined, false]]); + +var NumberNumberFun_NEQ1 = (a, b) => { return a != b; } +warmup(NumberNumberFun_NEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true], + [1,1, false], [3,4, true], [4294967295, 2, true], + [NaN, 2, true], [-1000, NaN, true], [undefined, null, false], + ['0', 0, false], [new String('0'), 0, false], [10.2, undefined, true]]); + +var NumberNumberFun_SEQ1 = (a, b) => { return a === b; } +warmup(NumberNumberFun_SEQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false], + [1,1, true], [3,4, false], [4294967295, 2, false], + [NaN, 2, false], [-1000, NaN, false], [undefined, null, false], + ['0', 0, false], [new String('0'), 0, false], [10.2, undefined, false]]); + +var NumberNumberFun_SNEQ1 = (a, b) => { return a !== b; } +warmup(NumberNumberFun_SNEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true], + [1,1, false], [3,4, true], [4294967295, 2, true], + [NaN, 2, true], [-1000, NaN, true], [undefined, null, true], + ['0', 0, true], [new String('0'), 0, true], [10.2, undefined, true]]); + +// Boolean + Int32 +var BooleanFun_GT1 = (a, b) => { return a > b; } +warmup(BooleanFun_GT1, [[1,2, false], [true, 2, false], [1,1, false], [true,true, false], [3,4, false], ]); + +var BooleanFun_GTE1 = (a, b) => { return a >= b; } +warmup(BooleanFun_GTE1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]); + +var BooleanFun_LT1 = (a, b) => { return a < b; } +warmup(BooleanFun_LT1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]); + +var BooleanFun_LTE1 = (a, b) => { return a <= b; } +warmup(BooleanFun_LTE1, [[1,2, true], [true, 2, true], [1,1, true], [true,true, true], [3,4, true]]); + +var BooleanFun_EQ1 = (a, b) => { return a == b; } +warmup(BooleanFun_EQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false], + ['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]); + +var BooleanFun_NEQ1 = (a, b) => { return a != b; } +warmup(BooleanFun_NEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true], + ['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]); + +var BooleanFun_SEQ1 = (a, b) => { return a === b; } +warmup(BooleanFun_SEQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]); + +var BooleanFun_SNEQ1 = (a, b) => { return a !== b; } +warmup(BooleanFun_SNEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]); + +// Undefined / Null equality. +var UndefNull_EQ1 = (a, b) => { return a == b; } +warmup(UndefNull_EQ1, [[undefined, null, true], [undefined, undefined, true], [null, undefined, true], + [null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]); + +var UndefNull_NEQ1 = (a, b) => { return a != b; } +warmup(UndefNull_NEQ1, [[undefined, null, false], [undefined, undefined, false], [null, undefined, false], + [null, null, false], [{a: 10}, undefined, true], [{a: 10}, null, true]]); + +var UndefNull_SEQ1 = (a, b) => { return a === b; } +warmup(UndefNull_SEQ1, [[undefined, null, false], [undefined, undefined, true], [null, undefined, false], + [null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]); + +var UndefNull_SNEQ1 = (a, b) => { return a !== b; } +warmup(UndefNull_SNEQ1, [[undefined, null, true], [undefined, undefined, false], [null, undefined, true], + [null, null, false], [{a: 10}, undefined, true], [{a: 10}, null, true]]); + +var typeDifference = function(a,b) { return a === b; } +warmup(typeDifference, [[1,1, true], [3,3, true], [3, typeDifference, false],[typeDifference, {}, false], + [3.2, 1, false], [0, -0, true]]); + +// String + Number +var String_Number_GT1 = (a, b) => { return a > b; } +warmup(String_Number_GT1, [["1.4",2, false], [1,"1", false], ["3",4, false], + [-1024, "-1023", false], [1.3, "1.2", true]]); + +var String_Number_GTE1 = (a, b) => { return a >= b; } +warmup(String_Number_GTE1, [["1.4",2, false], [1,"1", true], [3,"4", false], + [-1024, "-1023", false], [1.2, "1.2", true]]); + +var String_Number_LT1 = (a, b) => { return a < b; } +warmup(String_Number_LT1, [["1.4",2, true], ["1",1, false], [3,"4", true], + [-1024, "-1023", true], [1.2, "1.2", false]]); + +var String_Number_LTE1 = (a, b) => { return a <= b; } +warmup(String_Number_LTE1, [["1.4",2, true], ["1",1, true], [3,"4", true], + [-1024, "-1023", true], [1.2, "1.2", true]]); + +var String_Number_EQ1 = (a, b) => { return a == b; } +warmup(String_Number_EQ1, [["1.4",2, false], ["1",1, true], [3,"4", false], + [-1024, "-1023", false], [1.2, "1.2", true]]); + +var String_Number_NEQ1 = (a, b) => { return a != b; } +warmup(String_Number_NEQ1, [["1.4",2, true], ["1",1, false], [3,"4", true], + [-1024, "-1023", true], [1.2, "1.2", false]]); + +var String_Number_SEQ1 = (a, b) => { return a === b; } +warmup(String_Number_SEQ1, [["1.4",2, false], ["1",1, false], [3,"4", false], + [-1024, "-1023", false], [1.2, "1.2", false]]); + +var String_Number_SNEQ1 = (a, b) => { return a !== b; } +warmup(String_Number_SNEQ1, [["1.4",2, true], ["1",1, true], [3,"4", true], + [-1024, "-1023", true], [1.2, "1.2", true]]); + +// String + String +var String_String_GT1 = (a, b) => a > b; +warmup(String_String_GT1, [["", "", false], ["a", "a", false], ["aa", "aa", false], + ["a", "", true], ["", "a", false], + ["a", "b", false], ["b", "a", true], + ["a", "ab", false], ["ab", "a", true], + ]); + +var String_String_GE1 = (a, b) => a >= b; +warmup(String_String_GE1, [["", "", true], ["a", "a", true], ["aa", "aa", true], + ["a", "", true], ["", "a", false], + ["a", "b", false], ["b", "a", true], + ["a", "ab", false], ["ab", "a", true], + ]); + +var String_String_LT1 = (a, b) => a < b; +warmup(String_String_LT1, [["", "", false], ["a", "a", false], ["aa", "aa", false], + ["a", "", false], ["", "a", true], + ["a", "b", true], ["b", "a", false], + ["a", "ab", true], ["ab", "a", false], + ]); + +var String_String_LE1 = (a, b) => a <= b; +warmup(String_String_LE1, [["", "", true], ["a", "a", true], ["aa", "aa", true], + ["a", "", false], ["", "a", true], + ["a", "b", true], ["b", "a", false], + ["a", "ab", true], ["ab", "a", false], + ]); + +var String_String_EQ1 = (a, b) => a == b; +warmup(String_String_EQ1, [["", "", true], ["a", "a", true], ["aa", "aa", true], + ["a", "", false], ["", "a", false], + ["a", "b", false], ["b", "a", false], + ["a", "ab", false], ["ab", "a", false], + ]); + +var String_String_SEQ1 = (a, b) => a === b; +warmup(String_String_SEQ1, [["", "", true], ["a", "a", true], ["aa", "aa", true], + ["a", "", false], ["", "a", false], + ["a", "b", false], ["b", "a", false], + ["a", "ab", false], ["ab", "a", false], + ]); + +var String_String_NE1 = (a, b) => a != b; +warmup(String_String_NE1, [["", "", false], ["a", "a", false], ["aa", "aa", false], + ["a", "", true], ["", "a", true], + ["a", "b", true], ["b", "a", true], + ["a", "ab", true], ["ab", "a", true], + ]); + +var String_String_SNE1 = (a, b) => a !== b; +warmup(String_String_SNE1, [["", "", false], ["a", "a", false], ["aa", "aa", false], + ["a", "", true], ["", "a", true], + ["a", "b", true], ["b", "a", true], + ["a", "ab", true], ["ab", "a", true], + ]); + +// IsHTMLDDA internal slot +// https://tc39.github.io/ecma262/#sec-IsHTMLDDA-internal-slot +var IsHTMLDDA_EQ1 = (a, b) => a == b; +warmup(IsHTMLDDA_EQ1, [[createIsHTMLDDA(), null, true], + [createIsHTMLDDA(), undefined, true], + [null, createIsHTMLDDA(), true], + [undefined, createIsHTMLDDA(), true], + ]); diff --git a/js/src/jit-test/tests/cacheir/construct-bound-realm.js b/js/src/jit-test/tests/cacheir/construct-bound-realm.js new file mode 100644 index 0000000000..686fdc6c11 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/construct-bound-realm.js @@ -0,0 +1,10 @@ +function f() { + var g = newGlobal({sameCompartmentAs: this}); + g.evaluate(`function f() {}`); + var boundF = g.f.bind(null); + for (var i = 0; i < 50; i++) { + var obj = new boundF(); + assertEq(objectGlobal(obj), g); + } +} +f(); diff --git a/js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js b/js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js new file mode 100644 index 0000000000..38c7a998b5 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js @@ -0,0 +1,126 @@ +const types = [ + "Int8", + "Int16", + "Int32", + "Uint8", + "Uint16", + "Uint32", + "Float32", + "Float64", +]; + +function convert(type, value) { + let num = Number(value); + switch (type) { + case "Int8": + return ((num | 0) << 24) >> 24; + case "Int16": + return ((num | 0) << 16) >> 16; + case "Int32": + return (num | 0); + case "Uint8": + return (num >>> 0) & 0xff; + case "Uint16": + return (num >>> 0) & 0xffff; + case "Uint32": + return (num >>> 0); + case "Uint8Clamped": { + if (Number.isNaN(num)) { + return 0; + } + let clamped = Math.max(0, Math.min(num, 255)); + let f = Math.floor(clamped); + if (clamped < f + 0.5) { + return f; + } + if (clamped > f + 0.5) { + return f + 1; + } + return f + (f & 1); + } + case "Float32": + return Math.fround(num); + case "Float64": + return num; + } + throw new Error(); +} + + +function runTest(type, initial, values) { + let expected = values.map(v => convert(type, v)); + assertEq( + expected.some(e => Object.is(e, initial)), + false, + "initial must be different from the expected values" + ); + + // Create a fresh function to ensure ICs are specific to a single DataView function. + let test = Function("initial, values, expected", ` + let ab = new ArrayBuffer(16); + let dv = new DataView(ab); + for (let i = 0; i < 200; ++i) { + dv.set${type}(0, initial); + dv.set${type}(0, values[i % values.length]); + assertEq(dv.get${type}(0), expected[i % expected.length]); + } + `); + test(initial, values, expected); +} + +const tests = [ + // |null| is coerced to zero. + { + initial: 1, + values: [null], + }, + + // |undefined| is coerced to zero or NaN. + { + initial: 1, + values: [undefined], + }, + + // |false| is coerced to zero and |true| is coerced to one. + { + initial: 2, + values: [false, true], + }, + + // Strings without a fractional part. + { + initial: 42, + values: [ + "0", "1", "10", "111", "128", "256", "0x7fffffff", "0xffffffff", + ], + }, + + // Strings without a fractional part, but a leading minus sign. + { + initial: 42, + values: [ + "-0", "-1", "-10", "-111", "-128", "-256", "-2147483647", "-4294967295", + ], + }, + + // Strings with a fractional number part. + { + initial: 42, + values: [ + "0.1", "1.2", "10.8", "111.9", + "-0.1", "-1.2", "-10.8", "-111.9", + ], + }, + + // Special values and strings not parseable as a number. + { + initial: 42, + values: ["Infinity", "-Infinity", "NaN", "foobar"], + }, +]; + +for (let type of types) { + for (let {initial, values} of tests) { + runTest(type, initial, values); + } +} diff --git a/js/src/jit-test/tests/cacheir/dom-call.js b/js/src/jit-test/tests/cacheir/dom-call.js new file mode 100644 index 0000000000..e796bbbadd --- /dev/null +++ b/js/src/jit-test/tests/cacheir/dom-call.js @@ -0,0 +1,29 @@ +function simple() { + var obj = new FakeDOMObject(); + for (var i = 0; i < 10; i++) { + assertEq(obj.doFoo(0, 0, 0), 3); + } +} + +function wrongThis() { + var obj = new FakeDOMObject(); + var wrong = {doFoo: obj.doFoo}; + + for (var i = 0; i < 100; i++) { + assertEq(obj.doFoo(0, 0), i <= 50 ? 2 : undefined); + if (i == 50) { + obj = wrong; + } + } +} + +function spread() { + var obj = new FakeDOMObject(); + for (var i = 0; i < 10; i++) { + assertEq(obj.doFoo(...[1, 2, 3, 4]), 4); + } +} + +simple(); +wrongThis(); +spread();
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js new file mode 100644 index 0000000000..6558176f54 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js @@ -0,0 +1,38 @@ +// Function.prototype.apply is inlined as Function.prototype.call when it's +// called with less than two arguments. +// +// Test monomorphic calls. + +function testThisAbsent() { + for (let i = 0; i < 200; ++i) { + let r = Math.min.apply(); + assertEq(r, Infinity); + } +} +for (let i = 0; i < 2; ++i) testThisAbsent(); + +function test0() { + for (let i = 0; i < 200; ++i) { + let r = Math.min.apply(null); + assertEq(r, Infinity); + } +} +for (let i = 0; i < 2; ++i) test0(); + +// NOTE: We don't yet inline the case when the second argument is |null|. +function test1Null() { + for (let i = 0; i < 200; ++i) { + let r = Math.min.apply(null, null); + assertEq(r, Infinity); + } +} +for (let i = 0; i < 2; ++i) test1Null(); + +// NOTE: We don't yet inline the case when the second argument is |undefined|. +function test1Undefined() { + for (let i = 0; i < 200; ++i) { + let r = Math.min.apply(null, undefined); + assertEq(r, Infinity); + } +} +for (let i = 0; i < 2; ++i) test1Undefined(); diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js new file mode 100644 index 0000000000..b1295c99bd --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js @@ -0,0 +1,46 @@ +// Function.prototype.apply is inlined as Function.prototype.call when it's +// called with less than two arguments. +// +// Test polymorphic calls. + +function testThisAbsent() { + let xs = [Math.min, Math.max]; + let ys = [Infinity, -Infinity]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(); + assertEq(r, ys[i & 1]); + } +} +for (let i = 0; i < 2; ++i) testThisAbsent(); + +function test0() { + let xs = [Math.min, Math.max]; + let ys = [Infinity, -Infinity]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(null); + assertEq(r, ys[i & 1]); + } +} +for (let i = 0; i < 2; ++i) test0(); + +// NOTE: We don't yet inline the case when the second argument is |null|. +function test1Null() { + let xs = [Math.min, Math.max]; + let ys = [Infinity, -Infinity]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(null, null); + assertEq(r, ys[i & 1]); + } +} +for (let i = 0; i < 2; ++i) test1Null(); + +// NOTE: We don't yet inline the case when the second argument is |undefined|. +function test1Undefined() { + let xs = [Math.min, Math.max]; + let ys = [Infinity, -Infinity]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(null, undefined); + assertEq(r, ys[i & 1]); + } +} +for (let i = 0; i < 2; ++i) test1Undefined(); diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js new file mode 100644 index 0000000000..5d3cee4afa --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js @@ -0,0 +1,39 @@ +// Function.prototype.apply is inlined as Function.prototype.call when it's +// called with less than two arguments. +// +// Test inlining through specialised natives. + +// NOTE: We don't inline when |apply| is called with zero arguments. +function testThisAbsent() { + for (let i = 0; i < 200; ++i) { + let r = Array.apply(); + assertEq(r.length, 0); + } +} +for (let i = 0; i < 2; ++i) testThisAbsent(); + +function test0() { + for (let i = 0; i < 200; ++i) { + let r = Array.apply(null); + assertEq(r.length, 0); + } +} +for (let i = 0; i < 2; ++i) test0(); + +// NOTE: We don't yet inline the case when the second argument is |null|. +function test1Null() { + for (let i = 0; i < 200; ++i) { + let r = Array.apply(null, null); + assertEq(r.length, 0); + } +} +for (let i = 0; i < 2; ++i) test1Null(); + +// NOTE: We don't yet inline the case when the second argument is |undefined|. +function test1Undefined() { + for (let i = 0; i < 200; ++i) { + let r = Array.apply(null, undefined); + assertEq(r.length, 0); + } +} +for (let i = 0; i < 2; ++i) test1Undefined(); diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js new file mode 100644 index 0000000000..a0227ba81a --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js @@ -0,0 +1,42 @@ +// Function.prototype.apply is inlined as Function.prototype.call when it's +// called with less than two arguments. +// +// Test monomorphic calls. + +function one() { + return 1; +} + +function testThisAbsent() { + for (let i = 0; i < 200; ++i) { + let r = one.apply(); + assertEq(r, 1); + } +} +for (let i = 0; i < 2; ++i) testThisAbsent(); + +function test0() { + for (let i = 0; i < 200; ++i) { + let r = one.apply(null); + assertEq(r, 1); + } +} +for (let i = 0; i < 2; ++i) test0(); + +// NOTE: We don't yet inline the case when the second argument is |null|. +function test1Null() { + for (let i = 0; i < 200; ++i) { + let r = one.apply(null, null); + assertEq(r, 1); + } +} +for (let i = 0; i < 2; ++i) test1Null(); + +// NOTE: We don't yet inline the case when the second argument is |undefined|. +function test1Undefined() { + for (let i = 0; i < 200; ++i) { + let r = one.apply(null, undefined); + assertEq(r, 1); + } +} +for (let i = 0; i < 2; ++i) test1Undefined(); diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js new file mode 100644 index 0000000000..39ff35d96b --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js @@ -0,0 +1,50 @@ +// Function.prototype.apply is inlined as Function.prototype.call when it's +// called with less than two arguments. +// +// Test polymorphic calls. + +function one() { + return 1; +} + +function two() { + return 2; +} + +function testThisAbsent() { + let xs = [one, two]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(); + assertEq(r, 1 + (i & 1)); + } +} +for (let i = 0; i < 2; ++i) testThisAbsent(); + +function test0() { + let xs = [one, two]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(null); + assertEq(r, 1 + (i & 1)); + } +} +for (let i = 0; i < 2; ++i) test0(); + +// NOTE: We don't yet inline the case when the second argument is |null|. +function test1Null() { + let xs = [one, two]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(null, null); + assertEq(r, 1 + (i & 1)); + } +} +for (let i = 0; i < 2; ++i) test1Null(); + +// NOTE: We don't yet inline the case when the second argument is |undefined|. +function test1Undefined() { + let xs = [one, two]; + for (let i = 0; i < 200; ++i) { + let r = xs[i & 1].apply(null, undefined); + assertEq(r, 1 + (i & 1)); + } +} +for (let i = 0; i < 2; ++i) test1Undefined(); diff --git a/js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js b/js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js new file mode 100644 index 0000000000..b310df577b --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js @@ -0,0 +1,36 @@ +function testBasic() { + var thisVal = {}; + var arr = [1, 2, 3]; + var f = function() { + assertEq(this, thisVal); + assertEq(arguments.length, 0); + return 456; + }; + for (var i = 0; i < 100; i++) { + // Scripted callee. + assertEq(f.apply(thisVal), 456); + assertEq(f.apply(thisVal), 456); + assertEq(f.apply(thisVal, null), 456); + assertEq(f.apply(thisVal, undefined), 456); + + // Native callee. + assertEq(Math.abs.apply(), NaN); + assertEq(Math.abs.apply(null), NaN); + assertEq(Math.abs.apply(null, null), NaN); + assertEq(Array.prototype.join.apply(arr), "1,2,3"); + assertEq(Array.prototype.join.apply(arr, null), "1,2,3"); + assertEq(Array.prototype.join.apply(arr, undefined), "1,2,3"); + } +} +testBasic(); + +function testUndefinedGuard() { + var f = function() { return arguments.length; } + var arr = [-5, 5]; + for (var i = 0; i < 100; i++) { + var args = i < 50 ? undefined : arr; + assertEq(f.apply(null, args), i < 50 ? 0 : 2); + assertEq(Math.abs.apply(null, args), i < 50 ? NaN : 5); + } +} +testUndefinedGuard(); diff --git a/js/src/jit-test/tests/cacheir/fun-call-apply-weird.js b/js/src/jit-test/tests/cacheir/fun-call-apply-weird.js new file mode 100644 index 0000000000..ee1fe0e531 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-call-apply-weird.js @@ -0,0 +1,31 @@ +// |jit-test| --fast-warmup + +// Function with overridden call/apply (scripted). +function funOverridden1(x, y) { return x + y; } +funOverridden1.call = x => x + 1; +funOverridden1.apply = x => x + 2; + +// Function with overridden call/apply (native). +function funOverridden2(x, y) { return x + y; } +funOverridden2.call = Math.abs; +funOverridden2.apply = Math.abs; + +// Function with call/apply properties with other names. +function funOverridden3(x, y) { return x + y; } +funOverridden3.myCall = Function.prototype.call; +funOverridden3.myApply = Function.prototype.apply; + +function f() { + var arr = [1, 2]; + for (var i = 0; i < 100; i++) { + assertEq(funOverridden1.call(i, i), i + 1); + assertEq(funOverridden1.apply(i, i), i + 2); + + assertEq(funOverridden2.call(i, i), i); + assertEq(funOverridden2.apply(i, i), i); + + assertEq(funOverridden3.myCall(null, i, i), i + i); + assertEq(funOverridden3.myApply(null, arr), 3); + } +} +f(); diff --git a/js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js b/js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js new file mode 100644 index 0000000000..c7e6fd3183 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js @@ -0,0 +1,59 @@ +// Test inlining natives through Function.prototype.call +// +// Math.min() is inlined when there are 1-4 arguments. + +function mathMinThisAbsent() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(); + assertEq(r, Infinity); + } +} +for (let i = 0; i < 2; ++i) mathMinThisAbsent(); + +function mathMin0() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(null); + assertEq(r, Infinity); + } +} +for (let i = 0; i < 2; ++i) mathMin0(); + +function mathMin1() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(null, i); + assertEq(r, i); + } +} +for (let i = 0; i < 2; ++i) mathMin1(); + +function mathMin2() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(null, i, i + 1); + assertEq(r, i); + } +} +for (let i = 0; i < 2; ++i) mathMin2(); + +function mathMin3() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(null, i, i + 1, i + 2); + assertEq(r, i); + } +} +for (let i = 0; i < 2; ++i) mathMin3(); + +function mathMin4() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(null, i, i + 1, i + 2, i + 3); + assertEq(r, i); + } +} +for (let i = 0; i < 2; ++i) mathMin4(); + +function mathMin5() { + for (let i = 0; i < 400; ++i) { + let r = Math.min.call(null, i, i + 1, i + 2, i + 3, i + 4); + assertEq(r, i); + } +} +for (let i = 0; i < 2; ++i) mathMin5(); diff --git a/js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js b/js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js new file mode 100644 index 0000000000..13686e1119 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js @@ -0,0 +1,20 @@ +// Test inlining natives through Function.prototype.call + +function test(fn, expected) { + for (let i = 0; i < 400; ++i) { + let r = fn.call(null, 0, 1); + assertEq(r, expected); + } +} + +for (let i = 0; i < 2; ++i) { + let fn, expected; + if (i === 0) { + fn = Math.min; + expected = 0; + } else { + fn = Math.max; + expected = 1; + } + test(fn, expected); +} diff --git a/js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js b/js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js new file mode 100644 index 0000000000..5d396d426e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js @@ -0,0 +1,35 @@ +// Test inlining natives through Function.prototype.call +// +// Array() is inlined when there are 0-1 arguments. + +function arrayThisAbsent() { + for (let i = 0; i < 400; ++i) { + let r = Array.call(); + assertEq(r.length, 0); + } +} +for (let i = 0; i < 2; ++i) arrayThisAbsent(); + +function array0() { + for (let i = 0; i < 400; ++i) { + let r = Array.call(null); + assertEq(r.length, 0); + } +} +for (let i = 0; i < 2; ++i) array0(); + +function array1() { + for (let i = 0; i < 400; ++i) { + let r = Array.call(null, 5); + assertEq(r.length, 5); + } +} +for (let i = 0; i < 2; ++i) array1(); + +function array2() { + for (let i = 0; i < 400; ++i) { + let r = Array.call(null, 5, 10); + assertEq(r.length, 2); + } +} +for (let i = 0; i < 2; ++i) array2(); diff --git a/js/src/jit-test/tests/cacheir/function-length.js b/js/src/jit-test/tests/cacheir/function-length.js new file mode 100644 index 0000000000..14e852683f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/function-length.js @@ -0,0 +1,48 @@ +function interpreted() { + for (var i = 0; i < 50; i++) { + var f = function () {}; + assertEq(f.length, 0); + } + + for (var i = 0; i < 50; i++) { + var f = function (a, b) {}; + assertEq(f.length, 2); + } +} + +function bound() { + for (var i = 0; i < 50; i++) { + var f = (function () {}).bind({}); + assertEq(f.length, 0); + } + + for (var i = 0; i < 50; i++) { + var f = (function (a, b) {}).bind({}); + assertEq(f.length, 2); + } +} + +function native() { + for (var i = 0; i < 50; i++) { + // Use the interpreted function for getting the IC generated in the first place. + var f = function (a) {}; + + if (i == 15) { + f = Math.sin; + } else if (i == 20) { + f = Math.cos; + } else if (i == 25) { + f = Math.ceil; + } else if (i == 30) { + f = Math.tan; + } else if (i == 35) { + f = Math.tanh; + } + + assertEq(f.length, 1); + } +} + +interpreted(); +bound(); +native();
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/function-name.js b/js/src/jit-test/tests/cacheir/function-name.js new file mode 100644 index 0000000000..43b603c362 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/function-name.js @@ -0,0 +1,59 @@ +function interpreted() { + for (var i = 0; i < 50; i++) { + var f = (0, function () {}); + assertEq(f.name, ""); + } + + for (var i = 0; i < 50; i++) { + var f = function () {}; + assertEq(f.name, "f"); + } + + for (var i = 0; i < 50; i++) { + var f = function a () {}; + assertEq(f.name, "a"); + } +} + +function bound() { + for (var i = 0; i < 50; i++) { + var f = (function () {}).bind({}); + assertEq(f.name, "bound "); + } + + for (var i = 0; i < 50; i++) { + var f = (function a () {}).bind({}); + assertEq(f.name, "bound a"); + } +} + +function native() { + for (var i = 0; i < 50; i++) { + // Use the interpreted function for getting the IC generated in the first place. + var f = function () {}; + + var name = "f"; + if (i == 15) { + f = Math.sin; + name = "sin"; + } else if (i == 20) { + f = Math.cos; + name = "cos"; + } else if (i == 25) { + f = Math.ceil; + name = "ceil"; + } else if (i == 30) { + f = Math.tan; + name = "tan"; + } else if (i == 35) { + f = Math.tanh; + name = "tanh"; + } + + assertEq(f.name, name); + } +} + +interpreted(); +bound(); +native(); diff --git a/js/src/jit-test/tests/cacheir/generic-spreadcall.js b/js/src/jit-test/tests/cacheir/generic-spreadcall.js new file mode 100644 index 0000000000..bf1b053a53 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/generic-spreadcall.js @@ -0,0 +1,14 @@ +var sum = 0; + +function foo() { sum++; } + +const MAX_JIT_ARGS = 4096 / 8; +var arr = []; +for (var i = 0; i < MAX_JIT_ARGS + 1; i++) { + arr.push(1); +} + +for (var i = 0; i < 275; i++) { + foo(...arr); +} +assertEq(sum, 275); diff --git a/js/src/jit-test/tests/cacheir/getelem-undefined-null.js b/js/src/jit-test/tests/cacheir/getelem-undefined-null.js new file mode 100644 index 0000000000..0cf84155cc --- /dev/null +++ b/js/src/jit-test/tests/cacheir/getelem-undefined-null.js @@ -0,0 +1,52 @@ +function exists() { + var a = {'null': 1, 'undefined': 2}; + for (var i = 0; i < 100; i++) { + assertEq(a[null], 1); + assertEq(a[undefined], 2); + } +} + +function missing() { + var a = {}; + for (var i = 0; i < 100; i++) { + assertEq(a[null], undefined); + assertEq(a[undefined], undefined); + } +} + +function getter() { + var a = { + get null() { + return 1; + }, + get undefined() { + return 2; + } + } + for (var i = 0; i < 100; i++) { + assertEq(a[null], 1); + assertEq(a[undefined], 2); + } +} + +function primitive() { + var v = true; + for (var i = 0; i < 100; i++) { + assertEq(v[null], undefined); + assertEq(v[undefined], undefined); + } +} + +function mixed() { + var a = {'null': 'null', 'undefined': 'undefined'}; + for (var i = 0; i < 100; i++) { + var v = a[i % 2 ? null : undefined] + assertEq(a[v], i % 2 ? 'null' : 'undefined'); + } +} + +exists(); +missing() +getter(); +primitive(); +mixed(); diff --git a/js/src/jit-test/tests/cacheir/getpropsuper.js b/js/src/jit-test/tests/cacheir/getpropsuper.js new file mode 100644 index 0000000000..a2eec4646f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/getpropsuper.js @@ -0,0 +1,15 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +function testGetPropSuper() { + class C extends class { static p = 0 } { + static m() { + return super.p; + } + } + + for (var i = 0; i < 20; ++i) { + var v = C.m(); + assertEq(v, 0); + } +} +for (var i = 0; i < 2; ++i) testGetPropSuper(); diff --git a/js/src/jit-test/tests/cacheir/getter-primitive-value.js b/js/src/jit-test/tests/cacheir/getter-primitive-value.js new file mode 100644 index 0000000000..5dd188d5e1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/getter-primitive-value.js @@ -0,0 +1,116 @@ +function testNativeGetter() { + function test() { + var xs = [Symbol("a"), Symbol("b")]; + var ys = ["a", "b"]; + + for (var i = 0; i < 100; ++i) { + var r = xs[i & 1].description; + assertEq(r, ys[i & 1]); + } + } + for (var i = 0; i < 2; ++i) test(); +} +testNativeGetter(); + +function testScriptedGetter() { + Object.defineProperty(Symbol.prototype, "desc", { + get() { + "use strict"; + assertEq(typeof this, "symbol"); + return this.description; + } + }); + + function test() { + var xs = [Symbol("a"), Symbol("b")]; + var ys = ["a", "b"]; + + for (var i = 0; i < 100; ++i) { + var r = xs[i & 1].desc; + assertEq(r, ys[i & 1]); + } + } + for (var i = 0; i < 2; ++i) test(); +} +testScriptedGetter(); + +function testScriptedGetterNonStrict() { + Object.defineProperty(Symbol.prototype, "desc_nonstrict", { + get() { + assertEq(typeof this, "object"); + return this.description; + } + }); + + function test() { + var xs = [Symbol("a"), Symbol("b")]; + var ys = ["a", "b"]; + + for (var i = 0; i < 100; ++i) { + var r = xs[i & 1].desc_nonstrict; + assertEq(r, ys[i & 1]); + } + } + for (var i = 0; i < 2; ++i) test(); +} +testScriptedGetterNonStrict(); + +function testNativeGetterPrototype() { + Object.defineProperty(Object.prototype, "description_proto", + Object.getOwnPropertyDescriptor(Symbol.prototype, "description")); + + function test() { + var xs = [Symbol("a"), Symbol("b")]; + var ys = ["a", "b"]; + + for (var i = 0; i < 100; ++i) { + var r = xs[i & 1].description_proto; + assertEq(r, ys[i & 1]); + } + } + for (var i = 0; i < 2; ++i) test(); +} +testNativeGetterPrototype(); + +function testScriptedGetterPrototype() { + Object.defineProperty(Object.prototype, "desc_proto", { + get() { + "use strict"; + assertEq(typeof this, "symbol"); + return this.description; + } + }); + + function test() { + var xs = [Symbol("a"), Symbol("b")]; + var ys = ["a", "b"]; + + for (var i = 0; i < 100; ++i) { + var r = xs[i & 1].desc_proto; + assertEq(r, ys[i & 1]); + } + } + for (var i = 0; i < 2; ++i) test(); +} +testScriptedGetterPrototype(); + +function testScriptedGetterNonStrictPrototype() { + Object.defineProperty(Object.prototype, "desc_nonstrict_proto", { + get() { + assertEq(typeof this, "object"); + return this.description; + } + }); + + function test() { + var xs = [Symbol("a"), Symbol("b")]; + var ys = ["a", "b"]; + + for (var i = 0; i < 100; ++i) { + var r = xs[i & 1].desc_nonstrict_proto; + assertEq(r, ys[i & 1]); + } + } + for (var i = 0; i < 2; ++i) test(); +} +testScriptedGetterNonStrictPrototype(); diff --git a/js/src/jit-test/tests/cacheir/getter-setter-guards1.js b/js/src/jit-test/tests/cacheir/getter-setter-guards1.js new file mode 100644 index 0000000000..9ca64dd7da --- /dev/null +++ b/js/src/jit-test/tests/cacheir/getter-setter-guards1.js @@ -0,0 +1,64 @@ +// Objects with same shape but different getter/setter. +function testOwnProp() { + var count = 0; + var objects = [{get x() { count += 1; }, set x(v) { count += 2; }}, + {get x() { count += 3; }, set x(v) { count += 4; }}, + {get x() { count += 5; }, set x(v) { count += 6; }}]; + assertEq(shapeOf(objects[0]), shapeOf(objects[1])); + assertEq(shapeOf(objects[0]), shapeOf(objects[2])); + for (var i = 0; i < 150; i++) { + var o = objects[i % objects.length]; + o.x++; + } + assertEq(count, 1050); +} +testOwnProp(); + +// Ensure optimized TypedArray length properly deoptimizes when the getter is +// redefined. +function testTypedArrayLength() { + var ta = new Int32Array(10); + var lengthHolder = Object.getPrototypeOf(Int32Array.prototype); + for (var i = 0; i < 150; i++) { + assertEq(ta.length, i <= 100 ? 10 : (i <= 130 ? "foo" : "bar")); + assertEq(ta.byteLength, 40); + assertEq(ta.byteOffset, 0); + if (i === 100) { + var desc = Object.getOwnPropertyDescriptor(lengthHolder, "length"); + desc.get = () => "foo"; + Object.defineProperty(lengthHolder, "length", desc); + } + if (i === 130) { + var desc = Object.getOwnPropertyDescriptor(lengthHolder, "length"); + desc.get = () => "bar"; + Object.defineProperty(lengthHolder, "length", desc); + } + } +} +testTypedArrayLength(); + +// Native accessors on the global object. Redefine a few times and ensure the +// right function is called. Use |useWindowProxy: false| to allow optimizing all +// native getters. +function testGlobalProp() { + var g = newGlobal({useWindowProxy: false}); + g.evaluate("" + function test() { + var arr = [Math, Object]; + var expected = 0; + for (var i = 0; i < 150; i++) { + assertEq(timesAccessed, i <= 100 ? i + 1 : (i > 130 ? Infinity : NaN)); + if (i === 100) { + var desc = Object.getOwnPropertyDescriptor(this, "timesAccessed"); + desc.get = Math.abs; + Object.defineProperty(this, "timesAccessed", desc); + } + if (i === 130) { + var desc = Object.getOwnPropertyDescriptor(this, "timesAccessed"); + desc.get = Math.min; + Object.defineProperty(this, "timesAccessed", desc); + } + } + }); + g.evaluate("test()"); +} +testGlobalProp(); diff --git a/js/src/jit-test/tests/cacheir/getter-setter-guards2.js b/js/src/jit-test/tests/cacheir/getter-setter-guards2.js new file mode 100644 index 0000000000..1f6e79cce7 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/getter-setter-guards2.js @@ -0,0 +1,130 @@ +function testRedefineGetter() { + var callGetter = function(o) { + return o.x; + }; + + var proto = {get foo() {}, bar: 1}; + var obj = Object.create(proto); + + // Add "x" getter on the prototype. Warm up the IC. + var count1 = 0; + Object.defineProperty(proto, "x", {get: function(v) { + count1++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callGetter(obj); + } + assertEq(count1, 20); + + // Redefine "x" with a different getter. Ensure the new getter is called. + var count2 = 0; + Object.defineProperty(proto, "x", {get: function() { + count2++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callGetter(obj); + } + assertEq(count1, 20); + assertEq(count2, 20); +} +testRedefineGetter(); + +function testRedefineSetter() { + var callSetter = function(o) { + o.x = 1; + }; + + var proto = {get foo() {}, bar: 1}; + var obj = Object.create(proto); + + // Add "x" setter on the prototype. Warm up the IC. + var count1 = 0; + Object.defineProperty(proto, "x", {set: function(v) { + count1++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callSetter(obj); + } + assertEq(count1, 20); + + // Redefine "x" with a different setter. Ensure the new setter is called. + var count2 = 0; + Object.defineProperty(proto, "x", {set: function() { + count2++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callSetter(obj); + } + assertEq(count1, 20); + assertEq(count2, 20); +} +testRedefineSetter(); + +function testDeleteAdd() { + var callGetter = function(o) { + return o.x; + }; + + var proto = {get foo() {}, bar: 1}; + var obj = Object.create(proto); + + // Add "x" getter on the prototype. Warm up the IC. + var count1 = 0; + Object.defineProperty(proto, "x", {get: function() { + count1++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callGetter(obj); + } + assertEq(count1, 20); + + // Delete the getter. + delete proto.x; + + // Add "x" back with a different getter. Ensure the new getter is called. + var count2 = 0; + Object.defineProperty(proto, "x", {get: function() { + count2++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callGetter(obj); + } + assertEq(count1, 20); + assertEq(count2, 20); +} +testDeleteAdd(); + +function testAccessorToDataAndBack() { + var callGetter = function(o) { + return o.x; + }; + + var proto = {get foo() {}, bar: 1}; + var obj = Object.create(proto); + + // Add "x" getter on the prototype. Warm up the IC. + var count1 = 0; + Object.defineProperty(proto, "x", {get: function() { + count1++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callGetter(obj); + } + assertEq(count1, 20); + + // Turn the getter into a data property. + Object.defineProperty(proto, "x", {configurable: true, value: 123}); + + // Turn the data property into a (different) getter. Ensure the new getter is + // called. + var count2 = 0; + Object.defineProperty(proto, "x", {get: function() { + count2++; + }, configurable: true}); + for (var i = 0; i < 20; i++) { + callGetter(obj); + } + assertEq(count1, 20); + assertEq(count2, 20); +} +testAccessorToDataAndBack(); diff --git a/js/src/jit-test/tests/cacheir/global-getter.js b/js/src/jit-test/tests/cacheir/global-getter.js new file mode 100644 index 0000000000..cd8d0b22df --- /dev/null +++ b/js/src/jit-test/tests/cacheir/global-getter.js @@ -0,0 +1,36 @@ +// Tests for |this| value passed to getters defined on the global. + +function test(useWindowProxy) { + var g = newGlobal({useWindowProxy}); + g.useWindowProxy = useWindowProxy; + g.evaluate(` + var x = 123; + Object.defineProperty(this, "getX", {get: function() { return this.x; }}); + Object.defineProperty(Object.prototype, "protoGetX", {get: function() { return this.x * 2; }}); + Object.defineProperty(this, "thisIsProxy", {get: function() { return isProxy(this); }}); + + function f() { + for (var i = 0; i < 100; i++) { + // GetGName + assertEq(getX, 123); + assertEq(protoGetX, 246); + assertEq(thisIsProxy, useWindowProxy); + // GetProp + assertEq(globalThis.getX, 123); + assertEq(globalThis.protoGetX, 246); + assertEq(globalThis.thisIsProxy, useWindowProxy); + } + } + f(); + `); +} + +for (let useWindowProxy of [true, false]) { + test(useWindowProxy); +} + +setJitCompilerOption("ic.force-megamorphic", 1); + +for (let useWindowProxy of [true, false]) { + test(useWindowProxy); +} diff --git a/js/src/jit-test/tests/cacheir/global-setter.js b/js/src/jit-test/tests/cacheir/global-setter.js new file mode 100644 index 0000000000..19703104ee --- /dev/null +++ b/js/src/jit-test/tests/cacheir/global-setter.js @@ -0,0 +1,39 @@ +// Tests for |this| value passed to setters defined on the global. + +function test(useWindowProxy) { + var g = newGlobal({useWindowProxy}); + g.useWindowProxy = useWindowProxy; + g.evaluate(` + var x = 123; + Object.defineProperty(this, "checkX", + {set: function(v) { assertEq(this.x, v); }}); + Object.defineProperty(Object.prototype, "protoCheckX", + {set: function(v) { assertEq(this.x * 2, v); }}); + Object.defineProperty(this, "checkThisIsProxy", + {set: function(v) { assertEq(isProxy(this), v); }}); + + function f() { + for (var i = 0; i < 100; i++) { + // SetGName + checkX = 123; + protoCheckX = 246; + checkThisIsProxy = useWindowProxy; + // SetProp + globalThis.checkX = 123; + globalThis.protoCheckX = 246; + globalThis.checkThisIsProxy = useWindowProxy; + } + } + f(); + `); +} + +for (let useWindowProxy of [true, false]) { + test(useWindowProxy); +} + +setJitCompilerOption("ic.force-megamorphic", 1); + +for (let useWindowProxy of [true, false]) { + test(useWindowProxy); +} diff --git a/js/src/jit-test/tests/cacheir/has-sparse.js b/js/src/jit-test/tests/cacheir/has-sparse.js new file mode 100644 index 0000000000..2b3b2c8451 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/has-sparse.js @@ -0,0 +1,58 @@ +function sparse() { + var o = {0: 0, 0x10000: 0}; + + var tests = [[1, false], [0, true], [-2, false], [0x10000, true], [0x20000, false]]; + for (var [key, has] of tests) { + assertEq(key in o, has); + assertEq(o.hasOwnProperty(key), has); + } +} + +function typedArray() { + var o = {0: 0, 0x10000: 0}; + var t = new Int32Array(0x10001) + + // Only use Int32Array after we attached the sparse stub + // in o, in t + var tests = [[1, [false, true]], + [0, [true, true]], + [-2, [false, false]], + [0x10000, [true, true]], + [0x20000, [false, false]]]; + + for (var i = 0; i < 10; i++) { + for (var [key, has] of tests) { + assertEq(key in o, has[i > 5 ? 1 : 0]); + assertEq(o.hasOwnProperty(key), has[i > 5 ? 1 : 0]); + } + + if (i == 5) + o = t; + } +} + +function protoChange() { + var o = {0: 0, 0x10000: 0}; + + var tests = [[1, [false, true]], + [0, [true, true]], + [-2, [false, false]], + [0x10000, [true, true]], + [0x20000, [false, false]]]; + + for (var i = 0; i < 10; i++) { + for (var [key, has] of tests) { + assertEq(key in o, has[i > 5 ? 1 : 0]); + // Proto change doesn't affect hasOwnProperty. + assertEq(o.hasOwnProperty(key), has[0]); + } + + if (i == 5) + o.__proto__ = [1, 1, 1, 1]; + } +} + +sparse(); +typedArray(); +protoChange(); + diff --git a/js/src/jit-test/tests/cacheir/has-undefined-null.js b/js/src/jit-test/tests/cacheir/has-undefined-null.js new file mode 100644 index 0000000000..7f0109a5c1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/has-undefined-null.js @@ -0,0 +1,28 @@ +function exists() { + var a = {'null': 0, 'undefined': 0}; + for (var i = 0; i < 100; i++) { + assertEq(null in a, true); + assertEq(undefined in a, true); + } +} + +function missing() { + var a = {}; + for (var i = 0; i < 100; i++) { + assertEq(null in a, false); + assertEq(undefined in a, false); + } +} + +function mixed() { + var x = [{'null': 0}, {'undefined': 0}] + for (var i = 0; i < 100; i++) { + var a = x[i % 2]; + assertEq(null in a, i % 2 == 0); + assertEq(undefined in a, i % 2 == 1); + } +} + +exists(); +missing(); +mixed(); diff --git a/js/src/jit-test/tests/cacheir/has.js b/js/src/jit-test/tests/cacheir/has.js new file mode 100644 index 0000000000..9dba6330d8 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/has.js @@ -0,0 +1,66 @@ +var max = 40; +var key = "d"; +setJitCompilerOption("ion.warmup.trigger", max - 10); + +function simple() { + var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}]; + for (var i = 0; i < array.length; i++) { + var x = array[i]; + assertEq("a" in x, true); + assertEq("d" in x, false); + } +} + +function megamorphic() { + var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}, + {a: 1, b: 1}, {c: 1, e: 1, a: 1}, + {__proto__:{e: 1, f: 1, a: 1, g: 1}}, + {__proto__:{e: 1, f: 1, a: 1, g: 1, h: 1}}]; + for (var i = 0; i < array.length; i++) { + var x = array[i]; + assertEq("a" in x, true); + assertEq("d" in x, false); + } +} + +function protoSetProp() { + var base = {a: 1}; + var array = [{__proto__: base}, + {__proto__: base, b: 1, a: 1}, + {__proto__: base, c: 1, a: 1}]; + for (var j = 0; j < 2; j++) { + for (var i = 0; i < array.length; i++) { + var x = array[i]; + assertEq("a" in x, true); + assertEq("d" in x, (j > 0)); + } + base.d = 1; // Define property on prototype + } +} + +function protoSetElem() { + var base = {a: 1}; + var array = [{__proto__: base}, + {__proto__: base, b: 1, a: 1}, + {__proto__: base, c: 1, a: 1}]; + for (var j = 0; j < 2; j++) { + for (var i = 0; i < array.length; i++) { + var x = array[i]; + assertEq("a" in x, true); + assertEq("d" in x, (j > 0)); + } + base[key] = 1; // Define property on prototype + } +} + +function test() { + for (var i = 0; i < max; i++) { + simple(); + megamorphic(); + protoSetProp(); + protoSetElem(); + } +} + +test(); +test(); diff --git a/js/src/jit-test/tests/cacheir/hasown.js b/js/src/jit-test/tests/cacheir/hasown.js new file mode 100644 index 0000000000..5a3bf28d6d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/hasown.js @@ -0,0 +1,46 @@ +var max = 40; +setJitCompilerOption("ion.warmup.trigger", max - 10); + +function simple() { + var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}]; + for (var i = 0; i < array.length; i++) { + var x = array[i]; + assertEq(x.hasOwnProperty("a"), true); + assertEq(x.hasOwnProperty("d"), false); + } +} + +function megamorphic() { + var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}, + {a: 1, b: 1}, {c: 1, e: 1, a: 1}, + {e: 1, f: 1, a: 1, g: 1}, + {e: 1, f: 1, a: 1, g: 1, h: 1}]; + for (var i = 0; i < array.length; i++) { + var x = array[i]; + assertEq(x.hasOwnProperty("a"), true); + assertEq(x.hasOwnProperty("d"), false); + } +} + +function key() { + var sym = Symbol(), sym2 = Symbol(); + var keys = [[sym, true], [sym2, false], + ["a", true], ["b", false], + [{}, false]]; + var obj = {[sym]: 1, a: 1}; + for (var i = 0; i < keys.length; i++) { + var [key, result] = keys[i]; + assertEq(obj.hasOwnProperty(key), result); + } +} + +function test() { + for (var i = 0; i < max; i++) { + simple(); + megamorphic(); + key(); + } +} + +test(); +test(); diff --git a/js/src/jit-test/tests/cacheir/iter-megamorphic.js b/js/src/jit-test/tests/cacheir/iter-megamorphic.js new file mode 100644 index 0000000000..8fcfe7dcd5 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/iter-megamorphic.js @@ -0,0 +1,18 @@ +function testIter(v) { + var c = 0; + for (var p in v) { + c++; + } + assertEq(c === 0 || c === 1, true); + assertEq(c === 0, v === null || v === undefined); +} +function test() { + var vals = [{a: 1}, {b: 1}, {c: 1}, {d: 1}, null, undefined, + {e: 1}, {f: 1}, {g: 1}, {h: 1}, {i: 1}]; + for (var i = 0; i < 100; i++) { + for (var v of vals) { + testIter(v); + } + } +} +test(); diff --git a/js/src/jit-test/tests/cacheir/load-typed-element-bigint.js b/js/src/jit-test/tests/cacheir/load-typed-element-bigint.js new file mode 100644 index 0000000000..926cd9026e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/load-typed-element-bigint.js @@ -0,0 +1,100 @@ +// Different typed array types to ensure we emit a GetProp IC. +var xs = [ + new BigInt64Array(10), + new BigUint64Array(10), +]; + +// Load 0n value. +function loadConstantZero() { + var value = 0n; + + xs[0][0] = value; + xs[1][0] = value; + + var ys = [ + BigInt.asIntN(64, value), + BigInt.asUintN(64, value), + ]; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + assertEq(ta[0], ys[i & 1]); + } +} +loadConstantZero(); + +// Load non-negative BigInt using inline digits. +function loadInlineDigits() { + var value = 1n; + + xs[0][0] = value; + xs[1][0] = value; + + var ys = [ + BigInt.asIntN(64, value), + BigInt.asUintN(64, value), + ]; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + assertEq(ta[0], ys[i & 1]); + } +} +loadInlineDigits(); + +// Load negative BigInt using inline digits. +function loadInlineDigitsNegative() { + var value = -1n; + + xs[0][0] = value; + xs[1][0] = value; + + var ys = [ + BigInt.asIntN(64, value), + BigInt.asUintN(64, value), + ]; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + assertEq(ta[0], ys[i & 1]); + } +} +loadInlineDigitsNegative(); + +// Still inline digits, but now two digits on 32-bit platforms +function loadInlineDigitsTwoDigits() { + var value = 4294967296n; + + xs[0][0] = value; + xs[1][0] = value; + + var ys = [ + BigInt.asIntN(64, value), + BigInt.asUintN(64, value), + ]; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + assertEq(ta[0], ys[i & 1]); + } +} +loadInlineDigitsTwoDigits(); + +// Negative case of |storeInlineDigitsTwoDigits|. +function loadInlineDigitsTwoDigitsNegative() { + var value = -4294967296n; + + xs[0][0] = value; + xs[1][0] = value; + + var ys = [ + BigInt.asIntN(64, value), + BigInt.asUintN(64, value), + ]; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + assertEq(ta[0], ys[i & 1]); + } +} +loadInlineDigitsTwoDigitsNegative(); diff --git a/js/src/jit-test/tests/cacheir/map-get-bigint.js b/js/src/jit-test/tests/cacheir/map-get-bigint.js new file mode 100644 index 0000000000..5ddbacec15 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-get-bigint.js @@ -0,0 +1,88 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i + 1])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testInlineDigitsSameSign(n) { + var xs = [1n, 2n]; + var ys = [3n, 4n]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testInlineDigitsSameSign); + +function testInlineDigitsDifferentSign(n) { + var xs = [-1n, 2n]; + var ys = [1n, -2n]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testInlineDigitsDifferentSign); + +function testHeapDigitsSameSign(n) { + // Definitely uses heap digits. + var heap = 2n ** 1000n; + + var xs = [heap + 1n, heap + 2n]; + var ys = [heap + 3n, heap + 4n]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testHeapDigitsSameSign); + +function testHeapDigitsDifferentSign(n) { + // Definitely uses heap digits. + var heap = 2n ** 1000n; + + var xs = [-(heap + 1n), heap + 2n]; + var ys = [heap + 1n, -(heap + 2n)]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testHeapDigitsDifferentSign); diff --git a/js/src/jit-test/tests/cacheir/map-get-nongcthing.js b/js/src/jit-test/tests/cacheir/map-get-nongcthing.js new file mode 100644 index 0000000000..9e36c6b871 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-get-nongcthing.js @@ -0,0 +1,116 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i + 1])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testInt32(n) { + var xs = [1, 2]; + var ys = [3, 4]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testInt32); + +function testDouble(n) { + var xs = [Math.PI, Infinity]; + var ys = [Math.E, -Infinity]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testDouble); + +function testZero(n) { + var xs = [0, -0]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap([0], n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2); +} +runTest(testZero); + +function testNaN(n) { + var xs = [NaN, -NaN]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap([NaN], n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2); +} +runTest(testNaN); + +function testUndefinedAndNull(n) { + var xs = [undefined, null]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testUndefinedAndNull); + +function testBoolean(n) { + var xs = [true, false]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testBoolean); diff --git a/js/src/jit-test/tests/cacheir/map-get-object.js b/js/src/jit-test/tests/cacheir/map-get-object.js new file mode 100644 index 0000000000..076c28a7b5 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-get-object.js @@ -0,0 +1,31 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i + 1])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [{}, {}]; + var ys = [{}, {}]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(test); diff --git a/js/src/jit-test/tests/cacheir/map-get-string.js b/js/src/jit-test/tests/cacheir/map-get-string.js new file mode 100644 index 0000000000..1a25d09fd3 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-get-string.js @@ -0,0 +1,83 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i + 1])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testConstant(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testConstant); + +function testConstantFatInline(n) { + var xs = ["a", "b"].map(s => s.repeat(10)); + var ys = ["c", "d"].map(s => s.repeat(10)); + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testConstantFatInline); + +function testComputed(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + z = String.fromCharCode(z.charCodeAt(0)); + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testComputed); + +function testRope(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var map = createMap(xs.map(x => x.repeat(100)), n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3].repeat(100); + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(testRope); diff --git a/js/src/jit-test/tests/cacheir/map-get-symbol.js b/js/src/jit-test/tests/cacheir/map-get-symbol.js new file mode 100644 index 0000000000..face9e4c2b --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-get-symbol.js @@ -0,0 +1,31 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i + 1])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [Symbol(), Symbol()]; + var ys = [Symbol(), Symbol()]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, N / 2 + N / 4); +} +runTest(test); diff --git a/js/src/jit-test/tests/cacheir/map-get-value.js b/js/src/jit-test/tests/cacheir/map-get-value.js new file mode 100644 index 0000000000..0b5e288a06 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-get-value.js @@ -0,0 +1,31 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i + 1])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testPolymorphic(n) { + var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []]; + var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 128; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 15]; + var v = map.get(z); + if (v !== undefined) c += v; + } + assertEq(c, (8 * 9) / 2 * 8); +} +runTest(testPolymorphic); diff --git a/js/src/jit-test/tests/cacheir/map-has-bigint.js b/js/src/jit-test/tests/cacheir/map-has-bigint.js new file mode 100644 index 0000000000..5ae51a093f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-has-bigint.js @@ -0,0 +1,84 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testInlineDigitsSameSign(n) { + var xs = [1n, 2n]; + var ys = [3n, 4n]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testInlineDigitsSameSign); + +function testInlineDigitsDifferentSign(n) { + var xs = [-1n, 2n]; + var ys = [1n, -2n]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testInlineDigitsDifferentSign); + +function testHeapDigitsSameSign(n) { + // Definitely uses heap digits. + var heap = 2n ** 1000n; + + var xs = [heap + 1n, heap + 2n]; + var ys = [heap + 3n, heap + 4n]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testHeapDigitsSameSign); + +function testHeapDigitsDifferentSign(n) { + // Definitely uses heap digits. + var heap = 2n ** 1000n; + + var xs = [-(heap + 1n), heap + 2n]; + var ys = [heap + 1n, -(heap + 2n)]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testHeapDigitsDifferentSign); diff --git a/js/src/jit-test/tests/cacheir/map-has-nongcthing.js b/js/src/jit-test/tests/cacheir/map-has-nongcthing.js new file mode 100644 index 0000000000..8fc653564f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-has-nongcthing.js @@ -0,0 +1,110 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testInt32(n) { + var xs = [1, 2]; + var ys = [3, 4]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testInt32); + +function testDouble(n) { + var xs = [Math.PI, Infinity]; + var ys = [Math.E, -Infinity]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testDouble); + +function testZero(n) { + var xs = [0, -0]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap([0], n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testZero); + +function testNaN(n) { + var xs = [NaN, -NaN]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap([NaN], n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testNaN); + +function testUndefinedAndNull(n) { + var xs = [undefined, null]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testUndefinedAndNull); + +function testBoolean(n) { + var xs = [true, false]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testBoolean); diff --git a/js/src/jit-test/tests/cacheir/map-has-object.js b/js/src/jit-test/tests/cacheir/map-has-object.js new file mode 100644 index 0000000000..46366586da --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-has-object.js @@ -0,0 +1,30 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [{}, {}]; + var ys = [{}, {}]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(test); diff --git a/js/src/jit-test/tests/cacheir/map-has-string.js b/js/src/jit-test/tests/cacheir/map-has-string.js new file mode 100644 index 0000000000..84a139a9e2 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-has-string.js @@ -0,0 +1,79 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testConstant(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testConstant); + +function testConstantFatInline(n) { + var xs = ["a", "b"].map(s => s.repeat(10)); + var ys = ["c", "d"].map(s => s.repeat(10)); + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testConstantFatInline); + +function testComputed(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + z = String.fromCharCode(z.charCodeAt(0)); + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testComputed); + +function testRope(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var map = createMap(xs.map(x => x.repeat(100)), n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3].repeat(100); + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testRope); diff --git a/js/src/jit-test/tests/cacheir/map-has-symbol.js b/js/src/jit-test/tests/cacheir/map-has-symbol.js new file mode 100644 index 0000000000..c0268a3da1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-has-symbol.js @@ -0,0 +1,30 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [Symbol(), Symbol()]; + var ys = [Symbol(), Symbol()]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(test); diff --git a/js/src/jit-test/tests/cacheir/map-has-value.js b/js/src/jit-test/tests/cacheir/map-has-value.js new file mode 100644 index 0000000000..fddb1e8b5d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-has-value.js @@ -0,0 +1,30 @@ +// Return a new map, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createMap(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Map(xs.map((x, i) => [x, i])); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testPolymorphic(n) { + var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []]; + var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []]; + var zs = [...xs, ...ys]; + var map = createMap(xs, n); + + var N = 128; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 15]; + if (map.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testPolymorphic); diff --git a/js/src/jit-test/tests/cacheir/map-size.js b/js/src/jit-test/tests/cacheir/map-size.js new file mode 100644 index 0000000000..55c8bb3e82 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/map-size.js @@ -0,0 +1,43 @@ +function testEmpty() { + let map = new Map(); + for (let i = 0; i < 100; ++i) { + assertEq(map.size, 0); + } +} +for (let i = 0; i < 2; ++i) testEmpty(); + +function testSimple() { + let map = new Map([1, 2, 3, 4].entries()); + for (let i = 0; i < 100; ++i) { + assertEq(map.size, 4); + } +} +for (let i = 0; i < 2; ++i) testSimple(); + +function testWithDelete() { + for (let i = 0; i < 100; ++i) { + let a = [1, 2, 3, 4]; + let map = new Map(a.entries()); + for (let j = 0; j < a.length; ++j) { + assertEq(map.size, a.length - j); + map.delete(j); + assertEq(map.size, a.length - j - 1); + } + assertEq(map.size, 0); + } +} +for (let i = 0; i < 2; ++i) testWithDelete(); + +function testWithSet() { + for (let i = 0; i < 100; ++i) { + let a = [1, 2, 3, 4]; + let map = new Map(); + for (let j = 0; j < a.length; ++j) { + assertEq(map.size, j); + map.set(a[j], 0); + assertEq(map.size, j + 1); + } + assertEq(map.size, a.length); + } +} +for (let i = 0; i < 2; ++i) testWithSet(); diff --git a/js/src/jit-test/tests/cacheir/math-min-max.js b/js/src/jit-test/tests/cacheir/math-min-max.js new file mode 100644 index 0000000000..b7b9fbe86a --- /dev/null +++ b/js/src/jit-test/tests/cacheir/math-min-max.js @@ -0,0 +1,180 @@ +function test1ArgInt32() { + function test(x, expected) { + assertEq(Math.max(x), expected); + assertEq(Math.min(x), expected); + } + for (var i = 0; i < 20; i++) { + test(i, i); + } + // Fail Int32 guard. + test(true, 1); + test({}, NaN); +} +test1ArgInt32(); + +function test1ArgNumber() { + function test(x, expected) { + assertEq(Math.max(x), expected); + assertEq(Math.min(x), expected); + } + for (var i = 0; i < 20; i++) { + test(3.14, 3.14); + test(-0, -0); + test(i, i); + } + // Fail Number guard. + test(true, 1); + test({}, NaN); +} +test1ArgNumber(); + +function test1ArgInt32ThenNumber() { + function test(x, expected) { + assertEq(Math.max(x), expected); + assertEq(Math.min(x), expected); + } + for (var i = 0; i < 20; i++) { + test(i, i); + } + for (var i = 0; i < 10; i++) { + test(i * 3.14, i * 3.14); + } +} +test1ArgInt32ThenNumber(); + +function test2ArgsInt32() { + function test(x, y, expectedMax, expectedMin) { + assertEq(Math.max(x, y), expectedMax); + assertEq(Math.min(x, y), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(0, i, i, 0); + test(-9, -1, -1, -9); + test(0, 0, 0, 0); + } + // Fail Int32 guard. + test(0, "3", 3, 0); + test({}, 2, NaN, NaN); +} +test2ArgsInt32(); + +function test2ArgsNumber() { + function test(x, y, expectedMax, expectedMin) { + assertEq(Math.max(x, y), expectedMax); + assertEq(Math.min(x, y), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(1.1, 2.2, 2.2, 1.1); + test(8, NaN, NaN, NaN); + test(NaN, 8, NaN, NaN); + test(-0, i, i, -0); + test(Infinity, -0, Infinity, -0); + test(-Infinity, Infinity, Infinity, -Infinity); + } + // Fail Number guard. + test(-0, "3", 3, -0); + test({}, 2.1, NaN, NaN); +} +test2ArgsNumber(); + +function test2ArgsInt32ThenNumber() { + function test(x, y, expectedMax, expectedMin) { + assertEq(Math.max(x, y), expectedMax); + assertEq(Math.min(x, y), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(-1, i, i, -1); + } + for (var i = 0; i < 10; i++) { + test(-0, i, i, -0); + } +} +test2ArgsInt32ThenNumber(); + +function test3ArgsInt32() { + function test(a, b, c, expectedMax, expectedMin) { + assertEq(Math.max(a, b, c), expectedMax); + assertEq(Math.min(a, b, c), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(30, 100, i, 100, i); + test(i, 0, -2, i, -2); + } + // Fail Int32 guard. + test(0, 1, "2", 2, 0); + test(-0, 1, 2, 2, -0); +} +test3ArgsInt32(); + +function test3ArgsNumber() { + function test(a, b, c, expectedMax, expectedMin) { + assertEq(Math.max(a, b, c), expectedMax); + assertEq(Math.min(a, b, c), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(100, i, -0, 100, -0); + test(i, NaN, -1, NaN, NaN); + } + // Fail Number guard. + test(-0, "3", 1, 3, -0); + test("9", 1.1, 3, 9, 1.1); +} +test3ArgsNumber(); + +function test3ArgsInt32ThenNumber() { + function test(a, b, c, expectedMax, expectedMin) { + assertEq(Math.max(a, b, c), expectedMax); + assertEq(Math.min(a, b, c), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(30, 100, i, 100, i); + } + for (var i = 0; i < 10; i++) { + test(123.4, 100, i, 123.4, i); + } +} +test3ArgsInt32ThenNumber(); + +function test4ArgsInt32() { + function test(a, b, c, d, expectedMax, expectedMin) { + assertEq(Math.max(a, b, c, d), expectedMax); + assertEq(Math.min(a, b, c, d), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(30, 100, i, 0, 100, 0); + test(i, 0, -1, -2, i, -2); + } + // Fail Int32 guard. + test(0, 1, 2, "3", 3, 0); + test(-0, 1, 2, 3, 3, -0); +} +test4ArgsInt32(); + +function test4ArgsNumber() { + function test(a, b, c, d, expectedMax, expectedMin) { + assertEq(Math.max(a, b, c, d), expectedMax); + assertEq(Math.min(a, b, c, d), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(3.1, 100, i, -0, 100, -0); + test(i, NaN, -1, -2, NaN, NaN); + } + // Fail Number guard. + test(-0, 1, 2, "3", 3, -0); + test("9", 1.1, 2, 3, 9, 1.1); +} +test4ArgsNumber(); + +function test4ArgsInt32ThenNumber() { + function test(a, b, c, d, expectedMax, expectedMin) { + assertEq(Math.max(a, b, c, d), expectedMax); + assertEq(Math.min(a, b, c, d), expectedMin); + } + for (var i = 0; i < 20; i++) { + test(i << 1, i - 100, -1, -2, i * 2, i - 100); + } + for (var i = 0; i < 10; i++) { + test(i * 1.1, i, -0, 0, i * 1.1, -0); + } +} +test4ArgsInt32ThenNumber(); diff --git a/js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js b/js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js new file mode 100644 index 0000000000..b6098435a1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js @@ -0,0 +1,63 @@ +setJitCompilerOption("ic.force-megamorphic", 1); + +function testBasic() { + // Create various native objects with dense elements. + var objs = [[1, 2, 3], {0: 1, 1: 2, 2: 3}]; + var fun = x => x; + fun[0] = 1; + fun[1] = 2; + fun[2] = 3; + objs.push(fun); + for (var i = 0; i < 20; i++) { + var o = {}; + o["x" + i] = i; + o[0] = 1; + o[1] = 2; + if (i < 10) { + o[2] = 3; + } else { + // o[2] will be a hole. + o[3] = 4; + } + objs.push(o); + } + + // Access their dense elements. + for (var i = 0; i < 10; i++) { + for (var j = 0; j < objs.length; j++) { + var obj = objs[j]; + assertEq(obj[0], 1); + assertEq(obj[1], 2); + assertEq(obj[2], j < 13 ? 3 : undefined); + assertEq(obj[3], j >= 13 ? 4 : undefined); + assertEq(0 in obj, true); + assertEq(1 in obj, true); + assertEq(2 in obj, j < 13); + assertEq(3 in obj, j >= 13); + assertEq(Object.hasOwn(obj, 0), true); + assertEq(Object.hasOwn(obj, 1), true); + assertEq(Object.hasOwn(obj, 2), j < 13); + assertEq(Object.hasOwn(obj, 3), j >= 13); + } + } +} +testBasic(); + +function testNonNative() { + var arr = [1, 2, 3]; + var proxy = new Proxy({}, { + get(target, prop) { return 456; }, + has(target, prop) { return prop === "0"; }, + }); + for (var i = 0; i < 100; i++) { + var obj = i < 95 ? arr : proxy; + assertEq(obj[0], i < 95 ? 1 : 456); + assertEq(0 in obj, true); + assertEq(1 in obj, i < 95); + assertEq(4 in obj, false); + assertEq(Object.hasOwn(obj, 0), i < 95); + assertEq(Object.hasOwn(obj, 1), i < 95); + assertEq(Object.hasOwn(obj, 4), false); + } +} +testNonNative(); diff --git a/js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js new file mode 100644 index 0000000000..7ecbbe36c4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js @@ -0,0 +1,38 @@ +// Test failure when constructing a function whose |.prototype| property isn't an object. + +function Klass() { + this.prop = 1; +} + +// Save the original prototype. +const KlassPrototype = Klass.prototype; + +// Set the prototype to a non-object value. +Klass.prototype = null; + +const prototypes = [ + null, + KlassPrototype, +]; + +const N = 500; +let c = 0; + +for (let i = 0; i <= N; ++i) { + // Always perform a set to avoid a cold-code bailout. + let proto = prototypes[(i === N)|0]; + Klass.prototype = proto; + + // Create a new object. + let o = new Klass(); + + // Read a property from the new object to ensure it was correctly allocated + // and initialised. + c += o.prop; + + // The prototype defaults to %Object.prototype% when the |.prototype| + // property isn't an object. + assertEq(Object.getPrototypeOf(o), proto === null ? Object.prototype : KlassPrototype); +} + +assertEq(c, N + 1); diff --git a/js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js new file mode 100644 index 0000000000..5e146db4b5 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js @@ -0,0 +1,129 @@ +// Test constructing a function when the |.prototype| property isn't an object. + +function test(proto) { + function Klass() { + this.prop = 1; + } + Klass.prototype = proto; + + const N = 100; + + let c = 0; + for (let i = 0; i < N; ++i) { + // Create a new object. + let o = new Klass(); + + // Read a property from the new object to ensure it was correctly allocated + // and initialised. + c += o.prop; + + // The prototype defaults to %Object.prototype% when the |.prototype| + // property isn't an object. + assertEq(Object.getPrototypeOf(o), Object.prototype); + } + + assertEq(c, N); +} + +const primitivesTypes = [ + undefined, + null, + 123, + true, + "str", + Symbol(), + 123n, +]; + +for (let primitive of primitivesTypes) { + // Create a fresh function object to avoid type pollution. + let fn = Function(`return ${test}`)(); + + fn(primitive); +} + +// Repeat the test from above, but this time |Klass| is a cross-realm function. + +function testCrossRealm(proto) { + const otherGlobal = newGlobal(); + const Klass = otherGlobal.eval(` + function Klass() { + this.prop = 1; + } + Klass; + `); + Klass.prototype = proto; + + const N = 100; + + let c = 0; + for (let i = 0; i < N; ++i) { + // Create a new object. + let o = new Klass(); + + // Read a property from the new object to ensure it was correctly allocated + // and initialised. + c += o.prop; + + // The prototype defaults to %Object.prototype% when the |.prototype| + // property isn't an object. + assertEq(Object.getPrototypeOf(o), otherGlobal.Object.prototype); + } + + assertEq(c, N); +} + +for (let primitive of primitivesTypes) { + // Create a fresh function object to avoid type pollution. + let fn = Function(`return ${testCrossRealm}`)(); + + fn(primitive); +} + +// Repeat the test from above, but this time |Klass| is a cross-realm new.target. + +function testCrossRealmNewTarget(proto) { + const otherGlobal = newGlobal(); + const Klass = otherGlobal.eval(` + function Klass() {} + Klass; + `); + Klass.prototype = proto; + + class C { + constructor() { + this.prop = 1; + } + } + + class D extends C { + constructor() { + super(); + } + } + + const N = 100; + + let c = 0; + for (let i = 0; i < N; ++i) { + // Create a new object. + let o = Reflect.construct(D, [], Klass); + + // Read a property from the new object to ensure it was correctly allocated + // and initialised. + c += o.prop; + + // The prototype defaults to %Object.prototype% when the |.prototype| + // property isn't an object. + assertEq(Object.getPrototypeOf(o), otherGlobal.Object.prototype); + } + + assertEq(c, N); +} + +for (let primitive of primitivesTypes) { + // Create a fresh function object to avoid type pollution. + let fn = Function(`return ${testCrossRealmNewTarget}`)(); + + fn(primitive); +} diff --git a/js/src/jit-test/tests/cacheir/nukedCCW.js b/js/src/jit-test/tests/cacheir/nukedCCW.js new file mode 100644 index 0000000000..53a14c067f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/nukedCCW.js @@ -0,0 +1,40 @@ +function testNuke() { + var wrapper = evaluate("({a: 15, b: {c: 42}})", + {global: newGlobal({newCompartment: true, sameZoneAs: this})}); + + var i, error; + try { + for (i = 0; i < 150; i++) { + assertEq(wrapper.b.c, 42); + assertEq(wrapper.a, 15); + + if (i == 142) { + // Next access to wrapper.b should throw. + nukeCCW(wrapper); + } + } + } catch (e) { + error = e; + } + + assertEq(error.message.includes("dead object"), true); + assertEq(i, 143); +} + +function testSweep() { + var wrapper = evaluate("({a: 15, b: {c: 42}})", + {global: newGlobal({newCompartment: true})}); + var error; + nukeCCW(wrapper); + gczeal(8, 1); // Sweep zones separately + try { + // Next access to wrapper.b should throw. + wrapper.x = 4; + } catch (e) { + error = e; + } + assertEq(error.message.includes("dead object"), true); +} + +testNuke(); +testSweep(); diff --git a/js/src/jit-test/tests/cacheir/number-parseInt-double.js b/js/src/jit-test/tests/cacheir/number-parseInt-double.js new file mode 100644 index 0000000000..c4c133480b --- /dev/null +++ b/js/src/jit-test/tests/cacheir/number-parseInt-double.js @@ -0,0 +1,205 @@ +// Test inlining parseInt with a Double input. + +const doubleValues = [ + // Values around INT32_MIN. + -2147483648.5, + -2147483647.5, + -2147483646.5, + + // Negative values. + -65536.1, -65535.2, -256.3, -255.4, -100.5, -50.6, -10.7, + + // Values around zero. + -2.1, -1.1, -0, +0, 0.1, 1.1, 2.1, + + // Positive values. + 10.7, 50.6, 100.5, 255.4, 256.3, 65535.2, 65536.1, + + // Values around INT32_MAX. + 2147483645.5, + 2147483646.5, + 2147483647.5, +]; + +// Test double input without an explicit radix. +function testRadixAbsent() { + for (let i = 0; i < 200; ++i) { + let x = doubleValues[i % doubleValues.length]; + let y = x|0; + + let r = Number.parseInt(x); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testRadixAbsent(); + +// Test double input with radix=10. +function testRadixTen() { + for (let i = 0; i < 200; ++i) { + let x = doubleValues[i % doubleValues.length]; + + let r = Number.parseInt(x, 10); + assertEq(r, x|0); + } +} +for (let i = 0; i < 2; ++i) testRadixTen(); + +// Test double input in the exclusive range (0, 1.0e-6). +function testBadTooSmallPositive() { + const goodValues = [ + +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5, + -0, -1.5, -2.5, -3.5, -4.5, -5.5, + ]; + const badValues = [ + 9.999999999999997e-7, // parseInt(9.999999999999997e-7) is 9. + 1e-7, // parseInt(1e-7) is 1. + ]; + + const values = [ + ...goodValues, + ...badValues, + ]; + + for (let i = 0; i < 200; ++i) { + let xs = [goodValues, values][(i >= 150)|0]; + let x = xs[i % xs.length]; + let y; + if (0 < x && x < 1e-6) { + y = (String(x).match(/(.*)e.*/)[1])|0; + } else { + y = x|0; + } + + let r = Number.parseInt(x); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testBadTooSmallPositive(); + +// Test double input in the exclusive range (-1.0e-6, -0). +function testBadTooSmallNegative() { + const goodValues = [ + +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5, + -0, -1.5, -2.5, -3.5, -4.5, -5.5, + ]; + const badValues = [ + -9.999999999999997e-7, // parseInt(-9.999999999999997e-7) is -9. + -1e-7, // parseInt(-1e-7) is -1. + ]; + + const values = [ + ...goodValues, + ...badValues, + ]; + + for (let i = 0; i < 200; ++i) { + let xs = [goodValues, values][(i >= 150)|0]; + let x = xs[i % xs.length]; + let y; + if (-1e-6 < x && x < -0) { + y = (String(x).match(/(.*)e.*/)[1])|0; + } else { + y = x|0; + } + + let r = Number.parseInt(x); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testBadTooSmallNegative(); + +// Test double input in the exclusive range (-1, -1.0e-6). +function testBadNegativeZero() { + const goodValues = [ + +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5, + -0, -1.5, -2.5, -3.5, -4.5, -5.5, + ]; + const badValues = [ + -0.1, // parseInt(-0.1) is -0. + -0.5, // parseInt(-0.5) is -0. + -0.9, // parseInt(-0.9) is -0. + ]; + + const values = [ + ...goodValues, + ...badValues, + ]; + + for (let i = 0; i < 200; ++i) { + let xs = [goodValues, values][(i >= 150)|0]; + let x = xs[i % xs.length]; + let y; + if (-1 < x && x < 0) { + y = -0; + } else { + y = x|0; + } + + let r = Number.parseInt(x); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testBadNegativeZero(); + +// Test double input with infinity values. +function testBadInfinity() { + const goodValues = [ + +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5, + -0, -1.5, -2.5, -3.5, -4.5, -5.5, + ]; + const badValues = [ + Infinity, // parseInt(Infinity) is NaN + -Infinity, // parseInt(-Infinity) is NaN + ]; + + const values = [ + ...goodValues, + ...badValues, + ]; + + for (let i = 0; i < 200; ++i) { + let xs = [goodValues, values][(i >= 150)|0]; + let x = xs[i % xs.length]; + let y; + if (!Number.isFinite(x)) { + y = NaN; + } else { + y = x|0; + } + + let r = Number.parseInt(x); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testBadInfinity(); + +// Test double input with NaN values. +function testBadNaN() { + const goodValues = [ + +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5, + -0, -1.5, -2.5, -3.5, -4.5, -5.5, + ]; + const badValues = [ + NaN, // parseInt(NaN) is NaN + ]; + + const values = [ + ...goodValues, + ...badValues, + ]; + + for (let i = 0; i < 200; ++i) { + let xs = [goodValues, values][(i >= 150)|0]; + let x = xs[i % xs.length]; + let y; + if (!Number.isFinite(x)) { + y = NaN; + } else { + y = x|0; + } + + let r = Number.parseInt(x); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testBadNaN(); diff --git a/js/src/jit-test/tests/cacheir/number-parseInt-int32.js b/js/src/jit-test/tests/cacheir/number-parseInt-int32.js new file mode 100644 index 0000000000..42def800de --- /dev/null +++ b/js/src/jit-test/tests/cacheir/number-parseInt-int32.js @@ -0,0 +1,93 @@ +// Test inlining parseInt with an Int32 input. + +const int32Values = [ + // Values around INT32_MIN. + -2147483648, + -2147483647, + -2147483646, + + // Negative values. + -65536, -65535, -256, -255, -100, -50, -10, + + // Values around zero. + -2, -1, 0, 1, 2, + + // Positive values. + 10, 50, 100, 255, 256, 65535, 65536, + + // Values around INT32_MAX. + 2147483645, + 2147483646, + 2147483647, +]; + +// Test int32 input without an explicit radix. +function testRadixAbsent() { + for (let i = 0; i < 200; ++i) { + let x = int32Values[i % int32Values.length]; + assertEq(x, x|0, "x is an int32 value"); + + let r = Number.parseInt(x); + assertEq(r, x); + } +} +for (let i = 0; i < 2; ++i) testRadixAbsent(); + +// Test int32 input with radix=10. +function testRadixTen() { + for (let i = 0; i < 200; ++i) { + let x = int32Values[i % int32Values.length]; + assertEq(x, x|0, "x is an int32 value"); + + let r = Number.parseInt(x, 10); + assertEq(r, x); + } +} +for (let i = 0; i < 2; ++i) testRadixTen(); + +// Test int32 input with radix=16. (This case isn't currently inlined.) +function testRadixSixteen() { + for (let i = 0; i < 200; ++i) { + let x = int32Values[i % int32Values.length]; + assertEq(x, x|0, "x is an int32 value"); + + let expected = Math.sign(x) * Number("0x" + Math.abs(x).toString(10)); + + let r = Number.parseInt(x, 16); + assertEq(r, expected); + } +} +for (let i = 0; i < 2; ++i) testRadixSixteen(); + +// Test with variable radix. +function testRadixVariable() { + for (let i = 0; i < 200; ++i) { + let x = int32Values[i % int32Values.length]; + assertEq(x, x|0, "x is an int32 value"); + + let radix = [10, 16][(i > 100)|0]; + + let expected = x; + if (radix === 16) { + expected = Math.sign(x) * Number("0x" + Math.abs(x).toString(10)); + } + + let r = Number.parseInt(x, radix); + assertEq(r, expected); + } +} +for (let i = 0; i < 2; ++i) testRadixVariable(); + +// Test with int32 and double inputs. +function testBadInput() { + for (let i = 0; i < 200; ++i) { + let x = int32Values[i % int32Values.length]; + assertEq(x, x|0, "x is an int32 value"); + + let y = [x, NaN][(i > 150)|0]; + + let r = Number.parseInt(y, 10); + assertEq(r, y); + } +} +for (let i = 0; i < 2; ++i) testBadInput(); diff --git a/js/src/jit-test/tests/cacheir/number-parseInt-string.js b/js/src/jit-test/tests/cacheir/number-parseInt-string.js new file mode 100644 index 0000000000..7206c14db2 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/number-parseInt-string.js @@ -0,0 +1,144 @@ +// Test inlining parseInt with a String input. + +const stringInt32Values = [ + // Values around INT32_MIN. + "-2147483648", + "-2147483647", + "-2147483646", + + // Negative values. + "-65536", "-65535", "-256", "-255", "-100", "-50", "-10", + + // Values around zero. + "-2", "-1", "0", "1", "2", + + // Positive values. + "10", "50", "100", "255", "256", "65535", "65536", + + // Values around INT32_MAX. + "2147483645", + "2147483646", + "2147483647", +]; + +const stringInt32HexValues = [ + // Values around INT32_MIN. + "-0x80000000", + "-0x7fffffff", + "-0x7ffffffe", + + // Negative values. + "-0x10000", "-0xffff", "-0x100", "-0xff", "-0x64", "-0x32", "-0xa", + + // Values around zero. + "-0x2", "-0x1", "0x0", "0x1", "0x2", + + // Positive values. + "0xa", "0x32", "0x64", "0xff", "0x100", "0xffff", "0x10000", + + // Values around INT32_MAX. + "0x7ffffffd", + "0x7ffffffe", + "0x7fffffff", +]; + +// Test string-int32 input without an explicit radix. +function testRadixAbsent() { + for (let i = 0; i < 200; ++i) { + let x = stringInt32Values[i % stringInt32Values.length]; + assertEq(+x, x|0, "x is an int32 value"); + + let r = Number.parseInt(x); + assertEq(r, +x); + } +} +for (let i = 0; i < 2; ++i) testRadixAbsent(); + +// Test string-int32 hex input without an explicit radix. +function testRadixAbsentHex() { + for (let i = 0; i < 200; ++i) { + let x = stringInt32HexValues[i % stringInt32HexValues.length]; + + // String to number conversion doesn't support negative hex-strings, so we + // have to chop off the leading minus sign manually. + let y = x; + let sign = 1; + if (x.startsWith("-")) { + y = x.slice(1); + sign = -1; + } + + assertEq((+y) * sign, ((+y) * sign)|0, "x is an int32 hex value"); + + let r = Number.parseInt(x); + assertEq(r, (+y) * sign); + } +} +for (let i = 0; i < 2; ++i) testRadixAbsentHex(); + +// Test string-int32 input with radix=10. +function testRadixTen() { + for (let i = 0; i < 200; ++i) { + let x = stringInt32Values[i % stringInt32Values.length]; + assertEq(+x, x|0, "x is an int32 value"); + + let r = Number.parseInt(x, 10); + assertEq(r, +x); + } +} +for (let i = 0; i < 2; ++i) testRadixTen(); + +// Test string-int32 input with radix=16. (This case isn't currently inlined.) +function testRadixSixteen() { + for (let i = 0; i < 200; ++i) { + let x = stringInt32Values[i % stringInt32Values.length]; + assertEq(+x, x|0, "x is an int32 value"); + + let expected = Math.sign(x) * Number("0x" + Math.abs(x).toString(10)); + + let r = Number.parseInt(x, 16); + assertEq(r, expected); + } +} +for (let i = 0; i < 2; ++i) testRadixSixteen(); + +// Test string-int32 hex input with radix=16. (This case isn't currently inlined.) +function testRadixSixteenHex() { + for (let i = 0; i < 200; ++i) { + let x = stringInt32HexValues[i % stringInt32HexValues.length]; + + // String to number conversion doesn't support negative hex-strings, so we + // have to chop off the leading minus sign manually. + let y = x; + let sign = 1; + if (x.startsWith("-")) { + y = x.slice(1); + sign = -1; + } + + assertEq((+y) * sign, ((+y) * sign)|0, "x is an int32 hex value"); + + let r = Number.parseInt(x, 16); + assertEq(r, (+y) * sign); + } +} +for (let i = 0; i < 2; ++i) testRadixSixteenHex(); + +// Test with variable radix. +function testRadixVariable() { + for (let i = 0; i < 200; ++i) { + let x = stringInt32Values[i % stringInt32Values.length]; + assertEq(+x, x|0, "x is an int32 value"); + + let radix = [10, 16][(i > 100)|0]; + + let expected = +x; + if (radix === 16) { + expected = Math.sign(+x) * Number("0x" + Math.abs(+x).toString(10)); + } + + let r = Number.parseInt(x, radix); + assertEq(r, expected); + } +} +for (let i = 0; i < 2; ++i) testRadixVariable(); diff --git a/js/src/jit-test/tests/cacheir/number-toString.js b/js/src/jit-test/tests/cacheir/number-toString.js new file mode 100644 index 0000000000..ed076a6753 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/number-toString.js @@ -0,0 +1,44 @@ +function int32() { + var n = 42; + for (var i = 0; i < 100; i++) { + assertEq(n.toString(), "42"); + } +} + +function double() { + var n = 3.14; + for (var i = 0; i < 100; i++) { + assertEq(n.toString(), "3.14"); + } +} + +function number() { + var n = 1; + for (var i = 0; i < 100; i++) { + assertEq(n.toString(), i > 50 ? "3.14" : "1"); + if (i == 50) { + n = 3.14; + } + } +} + +function obj() { + var o = new Number(42); + for (var i = 0; i < 100; i++) { + assertEq(o.toString(), "42"); + } +} + +function overwritten() { + Number.prototype.toString = () => "haha"; + var n = 42; + for (var i = 0; i < 100; i++) { + assertEq(n.toString(), "haha"); + } +} + +int32(); +double(); +number(); +obj(); +overwritten(); diff --git a/js/src/jit-test/tests/cacheir/object-addprop-hook.js b/js/src/jit-test/tests/cacheir/object-addprop-hook.js new file mode 100644 index 0000000000..f138ed0b65 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/object-addprop-hook.js @@ -0,0 +1,18 @@ +function test() { + var sym = Symbol(); + for (var i = 0; i < 100; i++) { + var obj = newObjectWithAddPropertyHook(); + assertEq(obj._propertiesAdded, 0); + obj.x = 1; + obj.y = 2; + obj.z = 3; + obj[sym] = 4; + obj[0] = 1; + obj[1234567] = 1; + assertEq(obj._propertiesAdded, 6); + assertEq(obj.x, 1); + assertEq(obj[sym], 4); + assertEq(obj[0], 1); + } +} +test(); diff --git a/js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js b/js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js new file mode 100644 index 0000000000..7568f17885 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js @@ -0,0 +1,14 @@ +let capture = []; + +for (let i = 0; i <= 200; ++i) { + if (i === 100) { + enableTrackAllocations(); + } + + // Create a new object through `new Object` and capture the result. + capture[i & 1] = new Object(); + + // Ensure the allocation is properly tracked when inlining `new Object` in CacheIR. + let data = getAllocationMetadata(capture[i & 1]); + assertEq(data !== null, i >= 100); +} diff --git a/js/src/jit-test/tests/cacheir/object-constructor.js b/js/src/jit-test/tests/cacheir/object-constructor.js new file mode 100644 index 0000000000..144e732a09 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/object-constructor.js @@ -0,0 +1,76 @@ +// Test various inlinable Object constructor calls. + +function callNoArgs() { + for (let i = 0; i < 100; ++i) { + let obj = Object(); + + // Creates a new empty object. + assertEq(Reflect.getPrototypeOf(obj), Object.prototype); + assertEq(Reflect.ownKeys(obj).length, 0); + } +} +for (let i = 0; i < 2; ++i) callNoArgs(); + +function constructNoArgs() { + for (let i = 0; i < 100; ++i) { + let obj = new Object(); + + // Creates a new empty object. + assertEq(Reflect.getPrototypeOf(obj), Object.prototype); + assertEq(Reflect.ownKeys(obj).length, 0); + } +} +for (let i = 0; i < 2; ++i) constructNoArgs(); + +function funCallNoArgs() { + // NB: Function.prototype.call is only inlined when the thisValue argument is present. + const thisValue = null; + + for (let i = 0; i < 100; ++i) { + let obj = Object.call(thisValue); + + // Creates a new empty object. + assertEq(Reflect.getPrototypeOf(obj), Object.prototype); + assertEq(Reflect.ownKeys(obj).length, 0); + } +} +for (let i = 0; i < 2; ++i) funCallNoArgs(); + +function callObjectArg() { + let xs = [{}, {}]; + for (let i = 0; i < 100; ++i) { + let x = xs[i & 1]; + let obj = Object(x); + + // Returns the input object. + assertEq(obj, x); + } +} +for (let i = 0; i < 2; ++i) callObjectArg(); + +function constructObjectArg() { + let xs = [{}, {}]; + for (let i = 0; i < 100; ++i) { + let x = xs[i & 1]; + let obj = new Object(x); + + // Returns the input object. + assertEq(obj, x); + } +} +for (let i = 0; i < 2; ++i) constructObjectArg(); + +function funCallObjectArg() { + // NB: Function.prototype.call is only inlined when the thisValue argument is present. + const thisValue = null; + + let xs = [{}, {}]; + for (let i = 0; i < 100; ++i) { + let x = xs[i & 1]; + let obj = Object.call(thisValue, x); + + // Returns the input object. + assertEq(obj, x); + } +} +for (let i = 0; i < 2; ++i) funCallObjectArg(); diff --git a/js/src/jit-test/tests/cacheir/object-is-prototype-of.js b/js/src/jit-test/tests/cacheir/object-is-prototype-of.js new file mode 100644 index 0000000000..1ecc441f47 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/object-is-prototype-of.js @@ -0,0 +1,41 @@ +function testPrimitive() { + for (var i = 0; i < 100; ++i) { + // Null and undefined. + assertEq(Object.prototype.isPrototypeOf(null), false); + assertEq(Object.prototype.isPrototypeOf(void 0), false); + + // Primitive wrappers. + assertEq(String.prototype.isPrototypeOf(""), false); + assertEq(Number.prototype.isPrototypeOf(0), false); + assertEq(Boolean.prototype.isPrototypeOf(true), false); + assertEq(BigInt.prototype.isPrototypeOf(0n), false); + assertEq(Symbol.prototype.isPrototypeOf(Symbol.hasInstance), false); + } +} +testPrimitive(); + +function testObject() { + for (var i = 0; i < 100; ++i) { + assertEq(Object.prototype.isPrototypeOf({}), true); + assertEq(Object.prototype.isPrototypeOf([]), true); + + assertEq(Array.prototype.isPrototypeOf({}), false); + assertEq(Array.prototype.isPrototypeOf([]), true); + } +} +testObject(); + +function testProxy() { + var proxy = new Proxy({}, new Proxy({}, { + get(t, pk, r) { + assertEq(pk, "getPrototypeOf"); + return Reflect.get(t, pk, r); + } + })); + + for (var i = 0; i < 100; ++i) { + assertEq(Object.prototype.isPrototypeOf(proxy), true); + assertEq(Array.prototype.isPrototypeOf(proxy), false); + } +} +testProxy(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js new file mode 100644 index 0000000000..f22d5fab63 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js @@ -0,0 +1,13 @@ +(() => { + let returnCalled = false; + ({}).__proto__.return = () => { + returnCalled = true; + return { value: 3, done: true }; + }; + + assertEq(returnCalled, false); + let [a,b] = [1,2,3]; + assertEq(returnCalled, true); + assertEq(a, 1); + assertEq(b, 2); +})(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js new file mode 100644 index 0000000000..83c568dac0 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js @@ -0,0 +1,17 @@ +(() => { + let returnCalled = false; + + function foo() { + ({}).__proto__.return = () => { + returnCalled = true; + return { value: 3, done: true }; + }; + return 2; + } + + assertEq(returnCalled, false); + let [a,[b=foo()]] = [1,[],3]; + assertEq(returnCalled, true); + assertEq(a, 1); + assertEq(b, 2); +})(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js new file mode 100644 index 0000000000..3fe8593b54 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js @@ -0,0 +1,13 @@ +(() => { + let nextCalled = 0; + ([])[Symbol.iterator]().__proto__.next = () => { + nextCalled++; + return {value: nextCalled, done: false}; + }; + + assertEq(nextCalled, 0); + let [a,b] = [1,2,3]; + assertEq(nextCalled, 2); + assertEq(a, 1); + assertEq(b, 2); +})(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js new file mode 100644 index 0000000000..a1ee65e784 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js @@ -0,0 +1,36 @@ +(() => { + let iterablesBase = [ + [1,2], + [1,2,3], + [1,2,3], + [3,2,1], + ]; + + let iterables = []; + for (let i = 0; i < 1000; i++) { + iterables.push([...iterablesBase[i % iterablesBase.length]]); + } + + iterables.push(new Map([[1, 3], [2,4]]).keys()); + + function testDestructuringInitialization(a) { + let [x,y] = a; + return y; + } + + function testDestructuringAssignment(a) { + let x, y; + [x,y] = a; + return y; + } + + for (let i = 0; i < iterables.length; i++) { + assertEq(testDestructuringInitialization(iterables[i]), 2); + } + + // refresh the last iterator + iterables[iterables.length - 1] = new Map([[1, 3], [2,4]]).keys(); + for (let i = 0; i < iterables.length; i++) { + assertEq(testDestructuringAssignment(iterables[i]), 2); + } +})(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js new file mode 100644 index 0000000000..42aab01057 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js @@ -0,0 +1,19 @@ +(() => { + var returnCalled = false; + Object.defineProperty(globalThis, 'x', { + get() { + return 42; + }, + set(value) { + ({}).__proto__.return = () => { + returnCalled = true; + return { value: 3, done: true }; + }; + } + }); + + [x] = [1, 2]; + + assertEq(x, 42); + assertEq(returnCalled, true); +})(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js new file mode 100644 index 0000000000..059914afc1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js @@ -0,0 +1,7 @@ +(() => { + ({}).__proto__[1] = 2; + let [x,y] = [1]; + + assertEq(x, 1); + assertEq(y, undefined); +})(); diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js new file mode 100644 index 0000000000..e6e7a6269e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js @@ -0,0 +1,10 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +function f(arr) { + var [a, b] = arr; + return b; +} + +for (var i = 0; i < 10_000; ++i) { + assertEq(f([0, 1]), 1); +} diff --git a/js/src/jit-test/tests/cacheir/optimize-spread.js b/js/src/jit-test/tests/cacheir/optimize-spread.js new file mode 100644 index 0000000000..accd56ea13 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/optimize-spread.js @@ -0,0 +1,16 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +function testOptimizeSpread() { + function f(a, b) { + return a + b; + } + function g(...rest) { + return f(...rest); + } + + for (var i = 0; i < 20; ++i) { + var v = g(1, 2); + assertEq(v, 3); + } +} +for (var i = 0; i < 2; ++i) testOptimizeSpread(); diff --git a/js/src/jit-test/tests/cacheir/parseInt-double-truncate.js b/js/src/jit-test/tests/cacheir/parseInt-double-truncate.js new file mode 100644 index 0000000000..2b989f2624 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/parseInt-double-truncate.js @@ -0,0 +1,41 @@ +// Test some double-truncation edge cases with parseInt(double). + +function testPos1() { + with({}) {} + var fun = d => parseInt(d); + for (var i = 0; i < 2000; i++) { + assertEq(fun(i + 0.5), i); + } + assertEq(fun(0xf_ffff_ffff) + 345, 68719477080); +} +testPos1(); + +function testPos2() { + with({}) {} + var fun = d => parseInt(d); + for (var i = 0; i < 2000; i++) { + assertEq(fun(i + 0.5), i); + } + assertEq(fun(0x8000_0000) + 345, 2147483993); +} +testPos2(); + +function testNeg1() { + with({}) {} + var fun = d => parseInt(d); + for (var i = 0; i < 2000; i++) { + assertEq(fun(i + 0.5), i); + } + assertEq(fun(-0xf_ffff_ffff) - 345, -68719477080); +} +testNeg1(); + +function testNeg2() { + with({}) {} + var fun = d => parseInt(d); + for (var i = 0; i < 2000; i++) { + assertEq(fun(i + 0.5), i); + } + assertEq(fun(-0x8000_0001) - 345, -2147483994); +} +testNeg2(); diff --git a/js/src/jit-test/tests/cacheir/rope-char-at.js b/js/src/jit-test/tests/cacheir/rope-char-at.js new file mode 100644 index 0000000000..6b864035d7 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/rope-char-at.js @@ -0,0 +1,20 @@ +function test(a, b, firstCharCode) { + var s = newRope(a, b); + for (var i = 0; i < s.length; i++) { + assertEq(s.charCodeAt(i), firstCharCode + i); + assertEq(s.charAt(i), String.fromCharCode(firstCharCode + i)); + } + // charAt/charCodeAt support one-level deep ropes without linearizing. + assertEq(isRope(s), true); + assertEq(isRope(a), false); + assertEq(isRope(b), false); +} +test("abcdefghijkl", "mnopqrstuvwxyz", 97); +test("a", "bcdefghijklmnopqrstuvwxyz", 97); +test("abcdefghijklmnopqrstuvwxy", "z", 97); +test("0123456789:;<=>?@ABCDEFGHIJ", "K", 48); +test("\u00fe\u00ff", "\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010A", 0xfe); +test("\u1000\u1001\u1002", "\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100A\u100B\u100C", 4096); + +// charAt/charCodeAt stubs currently fail for nested ropes. +test("ab", newRope("cdefghijklmnop", "qrstuvwxyz{|}~"), 97); diff --git a/js/src/jit-test/tests/cacheir/set-has-bigint.js b/js/src/jit-test/tests/cacheir/set-has-bigint.js new file mode 100644 index 0000000000..24c8889e99 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-bigint.js @@ -0,0 +1,84 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testInlineDigitsSameSign(n) { + var xs = [1n, 2n]; + var ys = [3n, 4n]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testInlineDigitsSameSign); + +function testInlineDigitsDifferentSign(n) { + var xs = [-1n, 2n]; + var ys = [1n, -2n]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testInlineDigitsDifferentSign); + +function testHeapDigitsSameSign(n) { + // Definitely uses heap digits. + var heap = 2n ** 1000n; + + var xs = [heap + 1n, heap + 2n]; + var ys = [heap + 3n, heap + 4n]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testHeapDigitsSameSign); + +function testHeapDigitsDifferentSign(n) { + // Definitely uses heap digits. + var heap = 2n ** 1000n; + + var xs = [-(heap + 1n), heap + 2n]; + var ys = [heap + 1n, -(heap + 2n)]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testHeapDigitsDifferentSign); diff --git a/js/src/jit-test/tests/cacheir/set-has-nongcthing.js b/js/src/jit-test/tests/cacheir/set-has-nongcthing.js new file mode 100644 index 0000000000..a5a64e0333 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-nongcthing.js @@ -0,0 +1,110 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testInt32(n) { + var xs = [1, 2]; + var ys = [3, 4]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testInt32); + +function testDouble(n) { + var xs = [Math.PI, Infinity]; + var ys = [Math.E, -Infinity]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testDouble); + +function testZero(n) { + var xs = [0, -0]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var set = createSet([0], n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testZero); + +function testNaN(n) { + var xs = [NaN, -NaN]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var set = createSet([NaN], n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testNaN); + +function testUndefinedAndNull(n) { + var xs = [undefined, null]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testUndefinedAndNull); + +function testBoolean(n) { + var xs = [true, false]; + var ys = [1, -1]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testBoolean); diff --git a/js/src/jit-test/tests/cacheir/set-has-object.js b/js/src/jit-test/tests/cacheir/set-has-object.js new file mode 100644 index 0000000000..ea1b690d70 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-object.js @@ -0,0 +1,30 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [{}, {}]; + var ys = [{}, {}]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(test); diff --git a/js/src/jit-test/tests/cacheir/set-has-string-gczeal.js b/js/src/jit-test/tests/cacheir/set-has-string-gczeal.js new file mode 100644 index 0000000000..9ef81634d6 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-string-gczeal.js @@ -0,0 +1,17 @@ +// Ensure string hashing works correctly under gczeal=compact. + +gczeal(14) + +function test() { + var set = new Set(); + var c = 0; + var N = 1000; + for (var i = 0; i < N; ++i) { + var k = String.fromCodePoint(i); + set.add(k); + if (set.has(k)) c++; + } + assertEq(c, N); +} + +test(); diff --git a/js/src/jit-test/tests/cacheir/set-has-string.js b/js/src/jit-test/tests/cacheir/set-has-string.js new file mode 100644 index 0000000000..eac19378c4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-string.js @@ -0,0 +1,79 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testConstant(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testConstant); + +function testConstantFatInline(n) { + var xs = ["a", "b"].map(s => s.repeat(10)); + var ys = ["c", "d"].map(s => s.repeat(10)); + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testConstantFatInline); + +function testComputed(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + z = String.fromCharCode(z.charCodeAt(0)); + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testComputed); + +function testRope(n) { + var xs = ["a", "b"]; + var ys = ["c", "d"]; + var zs = [...xs, ...ys]; + var set = createSet(xs.map(x => x.repeat(100)), n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3].repeat(100); + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testRope); diff --git a/js/src/jit-test/tests/cacheir/set-has-symbol.js b/js/src/jit-test/tests/cacheir/set-has-symbol.js new file mode 100644 index 0000000000..35e4f11267 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-symbol.js @@ -0,0 +1,30 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [Symbol(), Symbol()]; + var ys = [Symbol(), Symbol()]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(test); diff --git a/js/src/jit-test/tests/cacheir/set-has-value.js b/js/src/jit-test/tests/cacheir/set-has-value.js new file mode 100644 index 0000000000..af03bf02e3 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-value.js @@ -0,0 +1,30 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function testPolymorphic(n) { + var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []]; + var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 128; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 15]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(testPolymorphic); diff --git a/js/src/jit-test/tests/cacheir/set-size.js b/js/src/jit-test/tests/cacheir/set-size.js new file mode 100644 index 0000000000..894ed83be7 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-size.js @@ -0,0 +1,43 @@ +function testEmpty() { + let set = new Set(); + for (let i = 0; i < 100; ++i) { + assertEq(set.size, 0); + } +} +for (let i = 0; i < 2; ++i) testEmpty(); + +function testSimple() { + let set = new Set([1, 2, 3, 4]); + for (let i = 0; i < 100; ++i) { + assertEq(set.size, 4); + } +} +for (let i = 0; i < 2; ++i) testSimple(); + +function testWithDelete() { + for (let i = 0; i < 100; ++i) { + let a = [1, 2, 3, 4]; + let set = new Set(a); + for (let j = 0; j < a.length; ++j) { + assertEq(set.size, a.length - j); + set.delete(a[j]); + assertEq(set.size, a.length - j - 1); + } + assertEq(set.size, 0); + } +} +for (let i = 0; i < 2; ++i) testWithDelete(); + +function testWithAdd() { + for (let i = 0; i < 100; ++i) { + let a = [1, 2, 3, 4]; + let set = new Set(); + for (let j = 0; j < a.length; ++j) { + assertEq(set.size, j); + set.add(a[j]); + assertEq(set.size, j + 1); + } + assertEq(set.size, a.length); + } +} +for (let i = 0; i < 2; ++i) testWithAdd(); diff --git a/js/src/jit-test/tests/cacheir/setelem-id-guard.js b/js/src/jit-test/tests/cacheir/setelem-id-guard.js new file mode 100644 index 0000000000..5f5ef82c58 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/setelem-id-guard.js @@ -0,0 +1,91 @@ +function setSlot() { + var o = {x: 1, y: 2}; + for (var i=0; i<100; i++) { + var p = (i & 1) ? "x" : "y"; + o[p] = i; + assertEq((i & 1) ? o.x : o.y, i); + } +} +setSlot(); + +function setUnboxed() { + var arr = []; + for (var i=0; i<100; i++) + arr.push({x: 1, y: 2}); + + for (var i=0; i<100; i++) { + var o = arr[i]; + var p = (i & 1) ? "x" : "y"; + o[p] = i; + assertEq((i & 1) ? o.x : o.y, i); + } +} +setUnboxed(); + +function setUnboxedExpando() { + var arr = []; + for (var i=0; i<100; i++) + arr.push({x: 1, y: 2}); + + for (var i=0; i<100; i++) { + var o = arr[i]; + o.a = 1; + o.b = 2; + var p = (i & 1) ? "a" : "b"; + o[p] = i; + o[p] = o[p] + 4; + assertEq((i & 1) ? o.a : o.b, i + 4); + } +} +setUnboxedExpando(); + +function setArrayLength() { + var arr = []; + for (var i=0; i<100; i++) { + var p = (i & 1) ? "length" : "x"; + arr[p] = i; + assertEq((i & 1) ? arr.length : arr.x, i); + } +} +setArrayLength(); + +function setter() { + var c = 0; + var o = {set x(i) { c += i; }, set y(i) { c += i + 2; }}; + for (var i=0; i<100; i++) { + var p = (i & 1) ? "x" : "y"; + o[p] = i; + } + assertEq(c, 5050); +} +setter(); + +function addSlot() { + for (var i=0; i<100; i++) { + var o = {}; + var p1 = (i & 1) ? "x" : "y"; + var p2 = (i % 3) ? "a" : "b"; + o[p1] = i; + o[p2] = i; + assertEq((i & 1) ? o.x : o.y, i); + assertEq((i % 3) ? o.a : o.b, i); + } +} +addSlot(); + +function addExpandoSlot() { + var arr = []; + for (var i=0; i<100; i++) + arr.push({c: 1, d: 2}); + + for (var i=0; i<100; i++) { + var o = arr[i]; + var p1 = (i & 1) ? "x" : "y"; + var p2 = (i % 3) ? "a" : "b"; + o[p1] = i; + o[p2] = i; + assertEq((i & 1) ? o.x : o.y, i); + assertEq((i % 3) ? o.a : o.b, i); + } +} +addExpandoSlot(); diff --git a/js/src/jit-test/tests/cacheir/setelem-undefined-null.js b/js/src/jit-test/tests/cacheir/setelem-undefined-null.js new file mode 100644 index 0000000000..894fad5fe6 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/setelem-undefined-null.js @@ -0,0 +1,50 @@ +function exists() { + var a = {'null': 0, 'undefined': 0}; + for (var i = 0; i < 100; i++) { + a[null] = i; + a[undefined] = i * 2; + assertEq(a['null'], i); + assertEq(a['undefined'], i * 2); + } +} + +function adding() { + for (var i = 0; i < 100; i++) { + var a = {}; + a[null] = i; + a[undefined] = i * 2; + assertEq(a['null'], i); + assertEq(a['undefined'], i * 2); + } +} + +function setter() { + var test = 0; + var a = { + set null(v) { + test = v; + }, + set undefined(v) { + test = v * 2; + } + } + for (var i = 0; i < 100; i++) { + a[null] = i; + assertEq(test, i); + a[undefined] = i; + assertEq(test, i * 2); + } +} + +function mixed() { + var a = {'null': void 0, 'undefined': void 0}; + for (var i = 0; i < 100; i++) { + a[i % 2 ? null : undefined] = i; + assertEq(a[i % 2 ? 'null' : 'undefined'], i) + } +} + +exists(); +adding() +setter(); +mixed(); diff --git a/js/src/jit-test/tests/cacheir/setgname-let.js b/js/src/jit-test/tests/cacheir/setgname-let.js new file mode 100644 index 0000000000..92345fb0df --- /dev/null +++ b/js/src/jit-test/tests/cacheir/setgname-let.js @@ -0,0 +1,93 @@ +let x = 2; + +function simple() { + for (var i = 0; i < 10; i++) { + x = i; + assertEq(x, i); + } +} + +function setname() { + function set(obj, v) { + with (obj) { + x = v; + } + } + + set({}, 100) + assertEq(x, 100); + set({x: 1}, 0); + assertEq(x, 100); + set({__proto__: {x: 1}}, 13); + assertEq(x, 100); +} + + +function noshadow() { + for (var i = 0; i < 20; i++) { + x = i; + assertEq(x, i); + + if (i == 10) { + globalThis.x = "haha"; + assertEq(x, 10); + } + } +} + +function uninitialized() { + for (var i = 0; i < 20; i++) { + var threw = false; + try { + undef = 2; + } catch { + threw = true; + } + assertEq(threw, true); + } +} + +function simpleStrict() { + "use strict"; + for (var i = 0; i < 10; i++) { + x = i; + assertEq(x, i); + } +} + +// No with in strict! + +function noshadowStrict() { + "use strict"; + for (var i = 0; i < 20; i++) { + x = i; + assertEq(x, i); + + if (i == 10) { + globalThis.x = "haha"; + assertEq(x, 10); + } + } +} + +function uninitializedStrict() { + for (var i = 0; i < 20; i++) { + var threw = false; + try { + undef = 2; + } catch { + threw = true; + } + assertEq(threw, true); + } +} + +simple(); +setname(); +noshadow(); +uninitialized(); +simpleStrict(); +noshadowStrict(); +uninitializedStrict(); + +let undef = 42; diff --git a/js/src/jit-test/tests/cacheir/setter-is-native.js b/js/src/jit-test/tests/cacheir/setter-is-native.js new file mode 100644 index 0000000000..28949b0640 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/setter-is-native.js @@ -0,0 +1,15 @@ +// Make sure we use an IC call. +setJitCompilerOption("ion.forceinlineCaches", 1); + +// Assume |eval| is always a native function. +var obj = Object.defineProperty({}, "prop", { + set: eval +}); + +var p; +for (let i = 0; i < 1000; ++i) { + // Call the native setter (eval). + obj.prop = `p = ${i}`; + + assertEq(p, i); +} diff --git a/js/src/jit-test/tests/cacheir/shape-teleporting-1.js b/js/src/jit-test/tests/cacheir/shape-teleporting-1.js new file mode 100644 index 0000000000..35deb8c34f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/shape-teleporting-1.js @@ -0,0 +1,128 @@ +// Receiver shadows +(function() { + function check(p) { return p.x; } + + let a = { x: "a" }; + let b = { __proto__: a }; + let c = { __proto__: b }; + let d = { __proto__: c }; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + d.x = "d"; + assertEq(check(d), "d"); +})(); + +// Intermediate proto shadows +(function() { + function check(p) { return p.x; } + + let a = { x: "a" }; + let b = { __proto__: a }; + let c = { __proto__: b }; + let d = { __proto__: c }; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + c.x = "c"; + assertEq(check(d), "c"); +})(); + +// Receiver proto changes +(function() { + function check(p) { return p.x; } + + let a = { x: "a" }; + let b = { __proto__: a }; + let c = { __proto__: b }; + let d = { __proto__: c }; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + d.__proto__ = { x: "?" }; + assertEq(check(d), "?"); +})(); + +// Intermediate proto changes +(function() { + function check(p) { return p.x; } + + let a = { x: "a" }; + let b = { __proto__: a }; + let c = { __proto__: b }; + let d = { __proto__: c }; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + c.__proto__ = { x: "?" }; + assertEq(check(d), "?"); +})(); + +// Uncacheable holder proto +(function() { + function check(p) { return p.x; } + + function Base() { this.x = "a"; } + let a = new Base; + a.__proto__ = new Object; + let b = { __proto__: a }; + let c = { __proto__: b }; + let d = { __proto__: c }; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + b.__proto__ = { x: "?" }; + assertEq(check(d), "?"); +})(); + +// Uncacheable intermediate proto +(function() { + function check(p) { return p.x; } + + function Base() { this.x = "a"; } + function Node() { } + + let a = new Base; + let b = new Node; b.__proto__ = a; + let c = { __proto__: b }; + let d = { __proto__: c }; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + b.__proto__ = { x: "?" }; + assertEq(check(d), "?"); +})(); + +// Uncacheable receiver proto +(function() { + function check(p) { return p.x; } + + function Base() { this.x = "a"; } + function Node() { } + + let a = new Base; + let b = { __proto__: a }; + let c = { __proto__: b }; + let d = new Node; d.__proto__ = c; + + assertEq(check(d), "a"); + assertEq(check(d), "a"); + d.__proto__ = { x: "?" }; + assertEq(check(d), "?"); +})(); + +// Uncacheable receiver proto (only receiver / holder) +(function() { + function check(p) { return p.x; } + + function Base() { this.x = "a"; } + function Node() { } + + let a = new Base; + let b = new Node; b.__proto__ = a; + + assertEq(check(b), "a"); + assertEq(check(b), "a"); + b.__proto__ = { x: "?" }; + assertEq(check(b), "?"); +})(); diff --git a/js/src/jit-test/tests/cacheir/shape-teleporting-2.js b/js/src/jit-test/tests/cacheir/shape-teleporting-2.js new file mode 100644 index 0000000000..1b103d7ec3 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/shape-teleporting-2.js @@ -0,0 +1,47 @@ +function A(name) { this.name = name; } +function B() { } +function C() { } + +B.prototype = A0 = new A("0"); +C.prototype = B0 = new B(); + +var A1 = new A("1"); +var A2 = new A("2"); + +var B1 = new B(); +var B2 = new B(); + +var C1 = new C(); +var C2 = new C(); + +// Object <-+- A0 <-+- B0 <-+ +// | | | +// +- A1 +- B1 +- C1 +// | | | +// +- A2 +- B2 +- C2 + +Object.setPrototypeOf(C1, B1); +Object.setPrototypeOf(C2, B2); + +Object.setPrototypeOf(B1, A1); +Object.setPrototypeOf(B2, A2); + +// Object <-+- A0 <--- B0 +// | +// +- A1 <--- B1 <--- C1 +// | +// +- A2 <--- B2 <--- C2 + + +function getName(o) { return o.name; } + +// Warm up JIT +for (var i = 0; i < 100; i++) { + getName(C1); +} + +assertEq(B1.name, "1"); +assertEq(B2.name, "2"); + +assertEq(getName(B1), "1"); +assertEq(getName(B2), "2"); diff --git a/js/src/jit-test/tests/cacheir/shape-teleporting-3.js b/js/src/jit-test/tests/cacheir/shape-teleporting-3.js new file mode 100644 index 0000000000..18349056c3 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/shape-teleporting-3.js @@ -0,0 +1,32 @@ +function test1() { + var o1 = {x: 1, y: 2, z: 3}; + var o2 = Object.create(o1); + var o3 = Object.create(o2); + o2.x = 2; // Ensure teleporting is invalidated for o1. + for (var i = 0; i < 30; i++) { + assertEq(o3.y, i > 20 ? -1 : 2); + if (i === 20) { + // Add a (second) shadowing property to o2. The property access + // above must detect this properly. + o2.y = -1; + } + } +} +test1(); + +function test2() { + var o1 = {x: 1, y: 2, z: 3}; + var o2 = Object.create(o1); + var o3 = Object.create(o2); + var o4 = Object.create(o3); + o2.x = 1; + o2.a = 2; + o3.a = 2; + for (var i = 0; i < 30; i++) { + assertEq(o4.y, i > 20 ? undefined : 2); + if (i === 20) { + o2.__proto__ = null; + } + } +} +test2(); diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-1.js b/js/src/jit-test/tests/cacheir/spread-minmax-1.js new file mode 100644 index 0000000000..b1bd6e5692 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/spread-minmax-1.js @@ -0,0 +1,23 @@ +function testMin(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.min(...arr); + } + return sum; +} + +function testMax(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.max(...arr); + } + return sum; +} + +// Attach Int32MinMaxArrayResult. +assertEq(testMin([1,2,3,4,5]), 50); +assertEq(testMax([1,2,3,4,5]), 250); + +// Verify that we handle an empty list correctly. +assertEq(testMin([]), Infinity); +assertEq(testMax([]), -Infinity); diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-2.js b/js/src/jit-test/tests/cacheir/spread-minmax-2.js new file mode 100644 index 0000000000..4cc3b0d9d6 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/spread-minmax-2.js @@ -0,0 +1,23 @@ +function testMin(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.min(...arr); + } + return sum; +} + +function testMax(arr) { + var sum = 0; + for (var i = 0; i < 50; i++) { + sum += Math.max(...arr); + } + return sum; +} + +// Attach Int32MinMaxArrayResult. +assertEq(testMin([1,2,3,4,5]), 50); +assertEq(testMax([1,2,3,4,5]), 250); + +// Verify that we handle a double element correctly. +assertEq(testMin([1,2,3.5,4,5]), 50); +assertEq(testMax([1,2,3.5,4,5]), 250); diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-3.js b/js/src/jit-test/tests/cacheir/spread-minmax-3.js new file mode 100644 index 0000000000..ae92e5e555 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/spread-minmax-3.js @@ -0,0 +1,25 @@ +function testMin(arr) { + return Math.min(...arr); +} + +function testMax(arr) { + return Math.max(...arr); +} + +with({}) {} + +// Warp-compile. +var sum = 0; +for (var i = 0; i < 50; i++) { + sum += testMin([1, 2.5, 3]); + sum += testMax([1, 2.5, 3]); +} +assertEq(sum, 200); + +// Test min/max with no arguments. +assertEq(testMin([]), Infinity); +assertEq(testMax([]), -Infinity); + +// Test NaN. +assertEq(testMin([1,NaN]), NaN); +assertEq(testMax([1,NaN]), NaN); diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-4.js b/js/src/jit-test/tests/cacheir/spread-minmax-4.js new file mode 100644 index 0000000000..b6e57b9550 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/spread-minmax-4.js @@ -0,0 +1,15 @@ +function f(...rest) { + for (var i = 0; i < 100; ++i) { + rest[0] = 0; + var v = Math.max(...rest); + + rest[0] = i; + var w = Math.max(...rest); + + assertEq(v, 0); + assertEq(w, i); + } +} +for (var i = 0; i < 2; ++i) { + f(0, 0); +} diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-5.js b/js/src/jit-test/tests/cacheir/spread-minmax-5.js new file mode 100644 index 0000000000..a787f16783 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/spread-minmax-5.js @@ -0,0 +1,12 @@ +function f() { + var arr = [[]]; + for (var i = 1; i < 101; i++) { + arr[0].push(i); + } + var res = 0; + for (var i = 0; i < 100; i++) { + res += Math.max(...arr[0]) + Math.min(...arr[0]); + } + assertEq(res, 10100); +} +f(); diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js new file mode 100644 index 0000000000..21ad5f5164 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js @@ -0,0 +1,55 @@ +// Store an element into a previous hole value and later add more elements +// exceeding the initialised length. Cover both mono- and polymorphic call +// sites. Change array length to non-extensible during execution. + +function testStoreDenseHole() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + store(array, i); + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testStoreDenseHole(); + +function testStoreDenseHolePoly() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.preventExtensions(array); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testStoreDenseHolePoly(); diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js new file mode 100644 index 0000000000..513fd6bc08 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js @@ -0,0 +1,55 @@ +// Store an element into a previous hole value and later add more elements +// exceeding the initialised length. Cover both mono- and polymorphic call +// sites. The array has a non-writable length at the start. + +function testStoreDenseHole() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + Object.defineProperty(array, "length", { + writable: false + }); + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + store(array, i); + } + + assertEq(array.length, 4); + for (var i = 0; i < 4; ++i) { + assertEq(array[i], i); + } + for (var i = 4; i < 10; ++i) { + assertEq(i in array, false); + } +} +testStoreDenseHole(); + +function testStoreDenseHolePoly() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + Object.defineProperty(array, "length", { + writable: false + }); + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 4); + for (var i = 0; i < 4; ++i) { + assertEq(array[i], i); + } + for (var i = 4; i < 10; ++i) { + assertEq(i in array, false); + } +} +testStoreDenseHolePoly(); diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js new file mode 100644 index 0000000000..468976332f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js @@ -0,0 +1,55 @@ +// Store an element into a previous hole value and later add more elements +// exceeding the initialised length. Cover both mono- and polymorphic call +// sites. Change array length to non-writable during execution. + +function testStoreDenseHole() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + store(array, i); + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testStoreDenseHole(); + +function testStoreDenseHolePoly() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + if (i === 5) { + Object.defineProperty(array, "length", {writable: false}); + } + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 5); + for (var i = 0; i < 5; ++i) { + assertEq(array[i], i); + } + for (var i = 5; i < 10; ++i) { + assertEq(i in array, false); + } +} +testStoreDenseHolePoly(); diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole.js new file mode 100644 index 0000000000..fece420056 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole.js @@ -0,0 +1,43 @@ +// Store an element into a previous hole value and later add more elements +// exceeding the initialised length. Cover both mono- and polymorphic call +// sites. + +function testStoreDenseHole() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + + function store(ar, index) { + ar[index] = index; + } + + for (var i = 0; i < 10; ++i) { + store(array, i); + } + + assertEq(array.length, 10); + for (var i = 0; i < 10; ++i) { + assertEq(array[i], i); + } +} +testStoreDenseHole(); + +function testStoreDenseHolePoly() { + var array = [/* hole */, /* hole */, /* hole */, /* hole */, ]; + + function store(ar, index) { + ar[index] = index; + } + + var objects = [array, {}]; + + for (var i = 0; i < 10; ++i) { + for (var j = 0; j < objects.length; ++j) { + store(objects[j], i); + } + } + + assertEq(array.length, 10); + for (var i = 0; i < 10; ++i) { + assertEq(array[i], i); + } +} +testStoreDenseHolePoly(); diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-bigint.js b/js/src/jit-test/tests/cacheir/store-typed-element-bigint.js new file mode 100644 index 0000000000..e46ca13f85 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-typed-element-bigint.js @@ -0,0 +1,131 @@ +// Different typed array types to ensure we emit a SetProp IC. +var xs = [ + new BigInt64Array(10), + new BigUint64Array(10), +]; + +// Store with 0n as rhs. +function storeConstantZero() { + var value = 0n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeConstantZero(); + +// Store non-negative BigInt using inline digits. +function storeInlineDigits() { + var value = 1n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeInlineDigits(); + +// Store negative BigInt using inline digits. +function storeInlineDigitsNegative() { + var value = -1n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeInlineDigitsNegative(); + +// Still inline digits, but now two digits on 32-bit platforms +function storeInlineDigitsTwoDigits() { + var value = 4294967296n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeInlineDigitsTwoDigits(); + +// Negative case of |storeInlineDigitsTwoDigits|. +function storeInlineDigitsTwoDigitsNegative() { + var value = -4294967296n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeInlineDigitsTwoDigitsNegative(); + +// Store BigInt using heap digits. +function storeHeapDigits() { + var value = 2n ** 1000n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeHeapDigits(); + +// Store negative BigInt using heap digits. +function storeHeapDigitsNegative() { + var value = -(2n ** 1000n); + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeHeapDigitsNegative(); + +// Store BigInt with first number requiring heap digits. +function storeFirstHeapDigits() { + var value = 2n ** 64n; + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeFirstHeapDigits(); + +// Store negative BigInt with first number requiring heap digits. +function storeFirstHeapDigitsNegative() { + var value = -(2n ** 64n); + + for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + ta[0] = value; + } + + assertEq(xs[0][0], BigInt.asIntN(64, value)); + assertEq(xs[1][0], BigInt.asUintN(64, value)); +} +storeFirstHeapDigitsNegative(); diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js b/js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js new file mode 100644 index 0000000000..a084a733f9 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js @@ -0,0 +1,15 @@ +// Different typed array types to ensure we emit a SetProp IC. +var xs = [ + new Float32Array(10), + new Float64Array(10), +]; + +for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + + // Store with constant rhs. + ta[0] = 0.1; +} + +assertEq(xs[0][0], Math.fround(0.1)); +assertEq(xs[1][0], 0.1); diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js b/js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js new file mode 100644 index 0000000000..948fc74783 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js @@ -0,0 +1,17 @@ +// Different typed array types to ensure we emit a SetProp IC. +var xs = [ + new Float32Array(10), + new Float64Array(10), +]; + +for (var i = 0; i < 100; ++i) { + var ta = xs[i & 1]; + + var v = +ta[0]; + + // Store with payload-register rhs. + ta[0] = ~v; +} + +assertEq(xs[0][0], 0); +assertEq(xs[1][0], 0); diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js b/js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js new file mode 100644 index 0000000000..dcaa059864 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js @@ -0,0 +1,20 @@ +// Different typed array types to ensure we emit a SetProp IC. +var xs = [ + new Float32Array(10), + new Float64Array(10), +]; + +function f(ta) { + for (var k = 0;;) { + // Store with payload-stack rhs. + ta[k] = k; + break; + } +} + +for (var i = 0; i < 100; ++i) { + f(xs[i & 1]); +} + +assertEq(xs[0][0], 0); +assertEq(xs[1][0], 0); diff --git a/js/src/jit-test/tests/cacheir/string-at-oob.js b/js/src/jit-test/tests/cacheir/string-at-oob.js new file mode 100644 index 0000000000..b7e71db87f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-at-oob.js @@ -0,0 +1,151 @@ +// Test String.prototype.at with out-of-bounds indices. + +function* characters(...ranges) { + for (let [start, end] of ranges) { + for (let i = start; i <= end; ++i) { + yield i; + } + } +} + +const empty = []; + +const ascii = [...characters( + [0x41, 0x5A], // A..Z + [0x61, 0x7A], // a..z +)]; + +const latin1 = [...characters( + [0xC0, 0xFF], // À..ÿ +)]; + +const twoByte = [...characters( + [0x100, 0x17E], // Ā..ž +)]; + +function atomize(s) { + return Object.keys({[s]: 0})[0]; +} + +function codePoints() { + return [empty, ascii, latin1, twoByte]; +} + +function toRope(s) { + // Ropes have at least two characters. + if (s.length < 2) { + return s; + } + if (s.length === 2) { + return newRope(s[0], s[1]); + } + return newRope(s[0], s.substring(1)); +} + +function makeStrings() { + let strings = codePoints() + .map(codePoints => String.fromCodePoint(...codePoints)) + .flatMap(x => [ + x, + toRope(x), + newString(x, {twoByte: true}), + atomize(x), + ]); + return strings; +} + +function testNegativeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.at(-1000); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexConstant(); +} + +function testNegativeIndexVariable() { + let indices = [-1000, -2000]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.at(indices[i & 1]); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexVariable(); +} + +function testNegativeOrValidIndex() { + let indices = [-1000, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.at(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : undefined)); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeOrValidIndex(); +} + +function testTooLargeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.at(1000); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexConstant(); +} + +function testTooLargeIndexVariable() { + let indices = [1000, 2000]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.at(indices[i & 1]); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexVariable(); +} + +function testTooLargeOrValidIndex() { + let indices = [1000, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.at(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : undefined)); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeOrValidIndex(); +} diff --git a/js/src/jit-test/tests/cacheir/string-at-rope.js b/js/src/jit-test/tests/cacheir/string-at-rope.js new file mode 100644 index 0000000000..aa41fa1440 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-at-rope.js @@ -0,0 +1,80 @@ +function makeRope() { + var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_"); + var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~"); + var rope = newRope(left, right); + return {left, right, rope}; +} + +// Load a character from the left rope child using a constant index. The input +// to String.prototype.at is always rope. +function testLeftChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.at(0); + assertEq(ch, "@"); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildConstant(); +} + +// Load a character from the right rope child using a constant index. The input +// to String.prototype.at is always rope. +function testRightChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.at(32); + assertEq(ch, "`"); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildConstant(); +} + +// Load a character from the left rope child using a variable index. The input +// to String.prototype.at is always rope. +function testLeftChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, rope} = makeRope(); + + var idx = i % left.length; + var ch = rope.at(idx); + assertEq(ch, String.fromCharCode(0x40 + idx)); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildVariable(); +} + +// Load a character from the right rope child using a variable index. The input +// to String.prototype.at is always rope. +function testRightChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, right, rope} = makeRope(); + + var idx = i % right.length; + var ch = rope.at(left.length + idx); + assertEq(ch, String.fromCharCode(0x60 + idx)); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildVariable(); +} + +// Load all characters from both child ropes. This covers the case when the +// call to String.prototype.at linearizes the rope. +function testBothChildren() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + for (var j = 0; j < rope.length; ++j) { + var ch = rope.at(j); + assertEq(ch, String.fromCharCode(0x40 + j)); + } + } +} +for (var i = 0; i < 2; ++i) { + testBothChildren(); +} diff --git a/js/src/jit-test/tests/cacheir/string-at.js b/js/src/jit-test/tests/cacheir/string-at.js new file mode 100644 index 0000000000..8dfb2f779e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-at.js @@ -0,0 +1,115 @@ +// Test String.prototype.at with negative and non-negative indices. + +function* characters(...ranges) { + for (let [start, end] of ranges) { + for (let i = start; i <= end; ++i) { + yield i; + } + } +} + +const empty = []; + +const ascii = [...characters( + [0x41, 0x5A], // A..Z + [0x61, 0x7A], // a..z +)]; + +const latin1 = [...characters( + [0xC0, 0xFF], // À..ÿ +)]; + +const twoByte = [...characters( + [0x100, 0x17E], // Ā..ž +)]; + +function atomize(s) { + return Object.keys({[s]: 0})[0]; +} + +function codePoints() { + return [empty, ascii, latin1, twoByte]; +} + +function toRope(s) { + // Ropes have at least two characters. + if (s.length < 2) { + return s; + } + if (s.length === 2) { + return newRope(s[0], s[1]); + } + return newRope(s[0], s.substring(1)); +} + +function makeStrings() { + let strings = codePoints() + .map(codePoints => String.fromCodePoint(...codePoints)) + .flatMap(x => [ + x, + toRope(x), + newString(x, {twoByte: true}), + atomize(x), + ]); + return strings; +} + +function testNonNegativeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = 0; + let ch = str.at(index); + let expected = str.charAt(index); + if (expected === "") expected = undefined; + assertEq(ch, expected); + } +} +for (let i = 0; i < 2; ++i) { + testNonNegativeIndexConstant(); +} + +function testNonNegativeIndex() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = i & 3; + let ch = str.at(index); + let expected = str.charAt(index); + if (expected === "") expected = undefined; + assertEq(ch, expected); + } +} +for (let i = 0; i < 2; ++i) { + testNonNegativeIndex(); +} + +function testNegativeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = -1; + let ch = str.at(index); + let expected = str.charAt(str.length + index); + if (expected === "") expected = undefined; + assertEq(ch, expected); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexConstant(); +} + +function testNegativeIndex() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = -(i & 3) - 1; + let ch = str.at(index); + let expected = str.charAt(str.length + index); + if (expected === "") expected = undefined; + assertEq(ch, expected); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndex(); +} diff --git a/js/src/jit-test/tests/cacheir/string-charAt-oob.js b/js/src/jit-test/tests/cacheir/string-charAt-oob.js new file mode 100644 index 0000000000..4de28d691a --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-charAt-oob.js @@ -0,0 +1,151 @@ +// Test String.prototype.charAt with out-of-bounds indices. + +function* characters(...ranges) { + for (let [start, end] of ranges) { + for (let i = start; i <= end; ++i) { + yield i; + } + } +} + +const empty = []; + +const ascii = [...characters( + [0x41, 0x5A], // A..Z + [0x61, 0x7A], // a..z +)]; + +const latin1 = [...characters( + [0xC0, 0xFF], // À..ÿ +)]; + +const twoByte = [...characters( + [0x100, 0x17E], // Ā..ž +)]; + +function atomize(s) { + return Object.keys({[s]: 0})[0]; +} + +function codePoints() { + return [empty, ascii, latin1, twoByte]; +} + +function toRope(s) { + // Ropes have at least two characters. + if (s.length < 2) { + return s; + } + if (s.length === 2) { + return newRope(s[0], s[1]); + } + return newRope(s[0], s.substring(1)); +} + +function makeStrings() { + let strings = codePoints() + .map(codePoints => String.fromCodePoint(...codePoints)) + .flatMap(x => [ + x, + toRope(x), + newString(x, {twoByte: true}), + atomize(x), + ]); + return strings; +} + +function testNegativeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charAt(-1); + assertEq(ch, ""); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexConstant(); +} + +function testNegativeIndexVariable() { + let indices = [-1, -2]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charAt(indices[i & 1]); + assertEq(ch, ""); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexVariable(); +} + +function testNegativeOrValidIndex() { + let indices = [-1, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.charAt(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : "")); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeOrValidIndex(); +} + +function testTooLargeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charAt(1000); + assertEq(ch, ""); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexConstant(); +} + +function testTooLargeIndexVariable() { + let indices = [1000, 2000]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charAt(indices[i & 1]); + assertEq(ch, ""); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexVariable(); +} + +function testTooLargeOrValidIndex() { + let indices = [1000, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.charAt(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : "")); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeOrValidIndex(); +} diff --git a/js/src/jit-test/tests/cacheir/string-charAt-rope.js b/js/src/jit-test/tests/cacheir/string-charAt-rope.js new file mode 100644 index 0000000000..ea656e132c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-charAt-rope.js @@ -0,0 +1,80 @@ +function makeRope() { + var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_"); + var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~"); + var rope = newRope(left, right); + return {left, right, rope}; +} + +// Load a character from the left rope child using a constant index. The input +// to String.prototype.charAt is always rope. +function testLeftChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.charAt(0); + assertEq(ch, "@"); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildConstant(); +} + +// Load a character from the right rope child using a constant index. The input +// to String.prototype.charAt is always rope. +function testRightChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.charAt(32); + assertEq(ch, "`"); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildConstant(); +} + +// Load a character from the left rope child using a variable index. The input +// to String.prototype.charAt is always rope. +function testLeftChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, rope} = makeRope(); + + var idx = i % left.length; + var ch = rope.charAt(idx); + assertEq(ch, String.fromCharCode(0x40 + idx)); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildVariable(); +} + +// Load a character from the right rope child using a variable index. The input +// to String.prototype.charAt is always rope. +function testRightChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, right, rope} = makeRope(); + + var idx = i % right.length; + var ch = rope.charAt(left.length + idx); + assertEq(ch, String.fromCharCode(0x60 + idx)); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildVariable(); +} + +// Load all characters from both child ropes. This covers the case when the +// call to String.prototype.charAt linearizes the rope. +function testBothChildren() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + for (var j = 0; j < rope.length; ++j) { + var ch = rope.charAt(j); + assertEq(ch, String.fromCharCode(0x40 + j)); + } + } +} +for (var i = 0; i < 2; ++i) { + testBothChildren(); +} diff --git a/js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js b/js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js new file mode 100644 index 0000000000..da9b826d94 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js @@ -0,0 +1,151 @@ +// Test String.prototype.charCodeAt with out-of-bounds indices. + +function* characters(...ranges) { + for (let [start, end] of ranges) { + for (let i = start; i <= end; ++i) { + yield i; + } + } +} + +const empty = []; + +const ascii = [...characters( + [0x41, 0x5A], // A..Z + [0x61, 0x7A], // a..z +)]; + +const latin1 = [...characters( + [0xC0, 0xFF], // À..ÿ +)]; + +const twoByte = [...characters( + [0x100, 0x17E], // Ā..ž +)]; + +function atomize(s) { + return Object.keys({[s]: 0})[0]; +} + +function codePoints() { + return [empty, ascii, latin1, twoByte]; +} + +function toRope(s) { + // Ropes have at least two characters. + if (s.length < 2) { + return s; + } + if (s.length === 2) { + return newRope(s[0], s[1]); + } + return newRope(s[0], s.substring(1)); +} + +function makeStrings() { + let strings = codePoints() + .map(codePoints => String.fromCodePoint(...codePoints)) + .flatMap(x => [ + x, + toRope(x), + newString(x, {twoByte: true}), + atomize(x), + ]); + return strings; +} + +function testNegativeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charCodeAt(-1); + assertEq(ch, NaN); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexConstant(); +} + +function testNegativeIndexVariable() { + let indices = [-1, -2]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charCodeAt(indices[i & 1]); + assertEq(ch, NaN); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexVariable(); +} + +function testNegativeOrValidIndex() { + let indices = [-1, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.charCodeAt(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? cp[index] : NaN)); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeOrValidIndex(); +} + +function testTooLargeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charCodeAt(1000); + assertEq(ch, NaN); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexConstant(); +} + +function testTooLargeIndexVariable() { + let indices = [1000, 2000]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.charCodeAt(indices[i & 1]); + assertEq(ch, NaN); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexVariable(); +} + +function testTooLargeOrValidIndex() { + let indices = [1000, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.charCodeAt(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? cp[index] : NaN)); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeOrValidIndex(); +} diff --git a/js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js b/js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js new file mode 100644 index 0000000000..645c023230 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js @@ -0,0 +1,80 @@ +function makeRope() { + var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_"); + var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~"); + var rope = newRope(left, right); + return {left, right, rope}; +} + +// Load a character from the left rope child using a constant index. The input +// to String.prototype.charCodeAt is always rope. +function testLeftChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.charCodeAt(0); + assertEq(ch, 0x40); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildConstant(); +} + +// Load a character from the right rope child using a constant index. The input +// to String.prototype.charCodeAt is always rope. +function testRightChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.charCodeAt(32); + assertEq(ch, 0x60); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildConstant(); +} + +// Load a character from the left rope child using a variable index. The input +// to String.prototype.charCodeAt is always rope. +function testLeftChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, rope} = makeRope(); + + var idx = i % left.length; + var ch = rope.charCodeAt(idx); + assertEq(ch, 0x40 + idx); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildVariable(); +} + +// Load a character from the right rope child using a variable index. The input +// to String.prototype.charCodeAt is always rope. +function testRightChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, right, rope} = makeRope(); + + var idx = i % right.length; + var ch = rope.charCodeAt(left.length + idx); + assertEq(ch, 0x60 + idx); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildVariable(); +} + +// Load all characters from both child ropes. This covers the case when the +// call to String.prototype.charCodeAt linearizes the rope. +function testBothChildren() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + for (var j = 0; j < rope.length; ++j) { + var ch = rope.charCodeAt(j); + assertEq(ch, 0x40 + j); + } + } +} +for (var i = 0; i < 2; ++i) { + testBothChildren(); +} diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-oob.js b/js/src/jit-test/tests/cacheir/string-codePointAt-oob.js new file mode 100644 index 0000000000..f456361b5e --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-codePointAt-oob.js @@ -0,0 +1,151 @@ +// Test String.prototype.codePointAt with out-of-bounds indices. + +function* characters(...ranges) { + for (let [start, end] of ranges) { + for (let i = start; i <= end; ++i) { + yield i; + } + } +} + +const empty = []; + +const ascii = [...characters( + [0x41, 0x5A], // A..Z + [0x61, 0x7A], // a..z +)]; + +const latin1 = [...characters( + [0xC0, 0xFF], // À..ÿ +)]; + +const twoByte = [...characters( + [0x100, 0x17E], // Ā..ž +)]; + +function atomize(s) { + return Object.keys({[s]: 0})[0]; +} + +function codePoints() { + return [empty, ascii, latin1, twoByte]; +} + +function toRope(s) { + // Ropes have at least two characters. + if (s.length < 2) { + return s; + } + if (s.length === 2) { + return newRope(s[0], s[1]); + } + return newRope(s[0], s.substring(1)); +} + +function makeStrings() { + let strings = codePoints() + .map(codePoints => String.fromCodePoint(...codePoints)) + .flatMap(x => [ + x, + toRope(x), + newString(x, {twoByte: true}), + atomize(x), + ]); + return strings; +} + +function testNegativeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.codePointAt(-1); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexConstant(); +} + +function testNegativeIndexVariable() { + let indices = [-1, -2]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.codePointAt(indices[i & 1]); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeIndexVariable(); +} + +function testNegativeOrValidIndex() { + let indices = [-1, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.codePointAt(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? cp[index] : undefined)); + } +} +for (let i = 0; i < 2; ++i) { + testNegativeOrValidIndex(); +} + +function testTooLargeIndexConstant() { + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.codePointAt(1000); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexConstant(); +} + +function testTooLargeIndexVariable() { + let indices = [1000, 2000]; + let strings = makeStrings(); + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let ch = str.codePointAt(indices[i & 1]); + assertEq(ch, undefined); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeIndexVariable(); +} + +function testTooLargeOrValidIndex() { + let indices = [1000, 0]; + let strings = makeStrings(); + + // Number of string kinds created in makeStrings. + const N = 4; + + let cpoints = codePoints(); + assertEq(strings.length, cpoints.length * N); + + for (let i = 0; i < 200; ++i) { + let str = strings[i % strings.length]; + let index = indices[i & 1]; + let ch = str.codePointAt(index); + + let cp = cpoints[Math.trunc((i % strings.length) / N)]; + assertEq(ch, (0 <= index && index < cp.length ? cp[index] : undefined)); + } +} +for (let i = 0; i < 2; ++i) { + testTooLargeOrValidIndex(); +} diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js b/js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js new file mode 100644 index 0000000000..adbc9995c1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js @@ -0,0 +1,88 @@ +function makeString(from) { + var codePoints = []; + for (var i = 0; i < 16; ++i) { + codePoints.push(from + i); + } + return String.fromCodePoint(...codePoints); +} + +function makeRope() { + var left = newRope(makeString(0x140), makeString(0x140 + 16)); + var right = newRope(makeString(0x160), makeString(0x160 + 16)); + var rope = newRope(left, right); + return {left, right, rope}; +} + +// Load a character from the left rope child using a constant index. The input +// to String.prototype.codePointAt is always rope. +function testLeftChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.codePointAt(0); + assertEq(ch, 0x140); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildConstant(); +} + +// Load a character from the right rope child using a constant index. The input +// to String.prototype.codePointAt is always rope. +function testRightChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.codePointAt(32); + assertEq(ch, 0x160); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildConstant(); +} + +// Load a character from the left rope child using a variable index. The input +// to String.prototype.codePointAt is always rope. +function testLeftChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, rope} = makeRope(); + + var idx = i % left.length; + var ch = rope.codePointAt(idx); + assertEq(ch, 0x140 + idx); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildVariable(); +} + +// Load a character from the right rope child using a variable index. The input +// to String.prototype.codePointAt is always rope. +function testRightChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, right, rope} = makeRope(); + + var idx = i % right.length; + var ch = rope.codePointAt(left.length + idx); + assertEq(ch, 0x160 + idx); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildVariable(); +} + +// Load all characters from both child ropes. This covers the case when the +// call to String.prototype.codePointAt linearizes the rope. +function testBothChildren() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + for (var j = 0; j < rope.length; ++j) { + var ch = rope.codePointAt(j); + assertEq(ch, 0x140 + j); + } + } +} +for (var i = 0; i < 2; ++i) { + testBothChildren(); +} diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-rope.js b/js/src/jit-test/tests/cacheir/string-codePointAt-rope.js new file mode 100644 index 0000000000..76e16c7ea3 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-codePointAt-rope.js @@ -0,0 +1,80 @@ +function makeRope() { + var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_"); + var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~"); + var rope = newRope(left, right); + return {left, right, rope}; +} + +// Load a character from the left rope child using a constant index. The input +// to String.prototype.codePointAt is always rope. +function testLeftChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.codePointAt(0); + assertEq(ch, 0x40); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildConstant(); +} + +// Load a character from the right rope child using a constant index. The input +// to String.prototype.codePointAt is always rope. +function testRightChildConstant() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + var ch = rope.codePointAt(32); + assertEq(ch, 0x60); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildConstant(); +} + +// Load a character from the left rope child using a variable index. The input +// to String.prototype.codePointAt is always rope. +function testLeftChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, rope} = makeRope(); + + var idx = i % left.length; + var ch = rope.codePointAt(idx); + assertEq(ch, 0x40 + idx); + } +} +for (var i = 0; i < 2; ++i) { + testLeftChildVariable(); +} + +// Load a character from the right rope child using a variable index. The input +// to String.prototype.codePointAt is always rope. +function testRightChildVariable() { + for (var i = 0; i < 200; ++i) { + var {left, right, rope} = makeRope(); + + var idx = i % right.length; + var ch = rope.codePointAt(left.length + idx); + assertEq(ch, 0x60 + idx); + } +} +for (var i = 0; i < 2; ++i) { + testRightChildVariable(); +} + +// Load all characters from both child ropes. This covers the case when the +// call to String.prototype.codePointAt linearizes the rope. +function testBothChildren() { + for (var i = 0; i < 200; ++i) { + var {rope} = makeRope(); + + for (var j = 0; j < rope.length; ++j) { + var ch = rope.codePointAt(j); + assertEq(ch, 0x40 + j); + } + } +} +for (var i = 0; i < 2; ++i) { + testBothChildren(); +} diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js b/js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js new file mode 100644 index 0000000000..961953a598 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js @@ -0,0 +1,103 @@ +function testLinear() { + var s = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}"; + + for (var i = 0; i < 200; ++i) { + // Iterate over all possible string indices, which includes reading + // unpaired lead surrogates. + var index = i % s.length; + + var c = s.codePointAt(index); + var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1); + assertEq(c, e); + } +} +for (var i = 0; i < 2; ++i) { + testLinear(); +} + +function testLinearOnlyValidCodePoints() { + var s = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}"; + + for (var i = 0; i < 200; ++i) { + // Iterator over all valid code point indices. (Code points are at all even + // string indices.) + var index = (i % s.length) & ~1; + + var c = s.codePointAt(index); + var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1); + assertEq(c, e); + } +} +for (var i = 0; i < 2; ++i) { + testLinearOnlyValidCodePoints(); +} + +function testRope() { + var left = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}"; + var right = "\u{10008}\u{10009}\u{1000A}\u{1000B}\u{1000C}\u{1000D}\u{1000E}\u{1000F}"; + + for (var i = 0; i < 200; ++i) { + var s = newRope(left, right); + + // Iterate over all possible string indices, which includes reading + // unpaired lead surrogates. + var index = i % s.length; + + var c = s.codePointAt(index); + var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1); + assertEq(c, e); + } +} +for (var i = 0; i < 2; ++i) { + testRope(); +} + +function testRopeOnlyValidCodePoints() { + var left = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}"; + var right = "\u{10008}\u{10009}\u{1000A}\u{1000B}\u{1000C}\u{1000D}\u{1000E}\u{1000F}"; + + for (var i = 0; i < 200; ++i) { + var s = newRope(left, right); + + // Iterator over all valid code point indices. (Code points are at all even + // string indices.) + var index = (i % s.length) & ~1; + + var c = s.codePointAt(index); + var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1); + assertEq(c, e); + } +} +for (var i = 0; i < 2; ++i) { + testRopeOnlyValidCodePoints(); +} + +function testUnpairedLead() { + var s = "\u{d800}\u{d801}\u{d802}\u{d803}\u{d804}\u{d805}\u{d806}\u{d807}"; + + for (var i = 0; i < 200; ++i) { + var index = i % s.length; + + var c = s.codePointAt(index); + var e = 0xd800 + index; + assertEq(c, e); + } +} +for (var i = 0; i < 2; ++i) { + testUnpairedLead(); +} + +function testUnpairedTail() { + var s = "\u{dc00}\u{dc01}\u{dc02}\u{dc03}\u{dc04}\u{dc05}\u{dc06}\u{dc07}"; + + for (var i = 0; i < 200; ++i) { + var index = i % s.length; + + var c = s.codePointAt(index); + var e = 0xdc00 + index; + assertEq(c, e); + } +} +for (var i = 0; i < 2; ++i) { + testUnpairedTail(); +} diff --git a/js/src/jit-test/tests/cacheir/string-concat-null-undef.js b/js/src/jit-test/tests/cacheir/string-concat-null-undef.js new file mode 100644 index 0000000000..6ef75dc181 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-concat-null-undef.js @@ -0,0 +1,31 @@ +function test() { + var nullUndefLhs = [ + [null, "foo", "nullfoo"], + [null, "bar", "nullbar"], + [null, "123", "null123"], + [null, "", "null"], + [undefined, "abc", "undefinedabc"], + [undefined, "1", "undefined1"], + [undefined, "", "undefined"], + ["abc", "def", "abcdef"], + ]; + for (var [lhs, rhs, expected] of nullUndefLhs) { + assertEq(lhs + rhs, expected); + } + var nullUndefRhs = [ + ["foo", undefined, "fooundefined"], + ["bar", undefined, "barundefined"], + ["123", undefined, "123undefined"], + ["", undefined, "undefined"], + ["abc", null, "abcnull"], + ["1", null, "1null"], + ["", null, "null"], + ["abc", "def", "abcdef"], + ]; + for (var [lhs, rhs, expected] of nullUndefRhs) { + assertEq(lhs + rhs, expected); + } +} +test(); +test(); +test(); diff --git a/js/src/jit-test/tests/cacheir/string-fromCharCode-double.js b/js/src/jit-test/tests/cacheir/string-fromCharCode-double.js new file mode 100644 index 0000000000..4db1787148 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-fromCharCode-double.js @@ -0,0 +1,17 @@ +// Test inlining String.fromCharCode with Double instead of Int32 inputs. + +function test() { + for (let i = 0; i < 0xffff; ++i) { + let expected = String.fromCharCode(i); + + // Ensure the Double input is truncated to Int32. + assertEq(String.fromCharCode(i + 0.5), expected); + + // The input is converted using ToUint16. + assertEq(String.fromCharCode(i + 0.5 + 0x10_000), expected); + + // Test ToUint16 conversion when adding INT32_MAX + 1. + assertEq(String.fromCharCode(i + 0x8000_0000), expected); + } +} +for (let i = 0; i < 2; ++i) test(); diff --git a/js/src/jit-test/tests/cacheir/string-fromcodepoint.js b/js/src/jit-test/tests/cacheir/string-fromcodepoint.js new file mode 100644 index 0000000000..254f41fd85 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-fromcodepoint.js @@ -0,0 +1,15 @@ +function f() { + for (var i = 0x10F000; i <= 0x10FFFF + 1; ++i) { + String.fromCodePoint(i); + } +} + +for (var i = 0; i < 2; ++i) { + var err; + try { + f(); + } catch (e) { + err = e; + } + assertEq(err instanceof RangeError, true); +} diff --git a/js/src/jit-test/tests/cacheir/string-int32-arith.js b/js/src/jit-test/tests/cacheir/string-int32-arith.js new file mode 100644 index 0000000000..8f39d3e124 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-int32-arith.js @@ -0,0 +1,56 @@ +function test(zero, one) { + assertEq(10 - zero, 10); + assertEq(10 - one, 9); + assertEq(zero - 0, 0); + assertEq(one - 1, 0); + + assertEq(10 * zero, 0); + assertEq(zero * 10, 0); + assertEq(10 * one, 10); + assertEq(one * 10, 10); + + assertEq(10 / one, 10); + assertEq(one / 1, 1); + assertEq(10 % one, 0); + assertEq(one % 1, 0); + + assertEq(10 ** one, 10); + assertEq(one ** 4, 1); + + assertEq(10 & zero, 0); + assertEq(zero & 10, 0); + assertEq(10 & one, 0); + assertEq(one & 10, 0); + + assertEq(10 | zero, 10); + assertEq(zero | 10, 10); + assertEq(10 | one, 11); + assertEq(one | 10, 11); + + assertEq(10 ^ zero, 10); + assertEq(zero ^ 10, 10); + assertEq(10 ^ one, 11); + assertEq(one ^ 10, 11); + + assertEq(10 << zero, 10); + assertEq(zero << 10, 0); + assertEq(10 << one, 20); + assertEq(one << 10, 1024); + + assertEq(10 >> zero, 10); + assertEq(zero >> 10, 0); + assertEq(10 >> one, 5); + assertEq(one >> 10, 0); + + assertEq(10 >>> zero, 10); + assertEq(zero >>> 10, 0); + assertEq(10 >>> one, 5); + assertEq(one >>> 10, 0); +} + +for (var i = 0; i < 10; i++) { + test(0, 1); + test('0', '1'); + test('0x0', '0x1'); + test('0.0', '1.0'); +} diff --git a/js/src/jit-test/tests/cacheir/string-lastIndexOf.js b/js/src/jit-test/tests/cacheir/string-lastIndexOf.js new file mode 100644 index 0000000000..f281d1eac1 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-lastIndexOf.js @@ -0,0 +1,102 @@ +function testEmpty() { + var strings = [ + "", + "a", + "ab", + ]; + + for (var i = 0; i < 200; ++i) { + var str = strings[i % strings.length]; + assertEq(str.lastIndexOf(""), str.length); + } +} +testEmpty(); + +function testSingle() { + var strings = [ + "", + + "a", + "b", + + "aa", + "ab", + "ba", + "bb", + ]; + + var searchStrings = [ + "a", + "b", + ]; + + for (var i = 0; i < 200; ++i) { + var str = strings[i % strings.length]; + var searchString = searchStrings[i % searchStrings.length]; + + var j = str.length; + while (--j >= 0 && str[j] !== searchString); + + assertEq(str.lastIndexOf(searchString), j); + } +} +testSingle(); + +function testDouble() { + var strings = [ + "", + + "a", + "b", + + "aa", + "ab", + "ba", + "bb", + + "aaa", + "aab", + "aba", + "abb", + "baa", + "bab", + "bba", + "bbb", + + "aaaa", + "aaab", + "aaba", + "aabb", + "abaa", + "abab", + "abba", + "abbb", + + "baaa", + "baab", + "baba", + "babb", + "bbaa", + "bbab", + "bbba", + "bbbb", + ]; + + var searchStrings = [ + "aa", + "ab", + "ba", + "bb", + ]; + + for (var i = 0; i < 200; ++i) { + var str = strings[i % strings.length]; + var searchString = searchStrings[i % searchStrings.length]; + + var j = str.length; + while (--j >= 0 && (str[j] !== searchString[0] || str[j + 1] !== searchString[1])); + + assertEq(str.lastIndexOf(searchString), j); + } +} +testDouble(); diff --git a/js/src/jit-test/tests/cacheir/string-loadchar.js b/js/src/jit-test/tests/cacheir/string-loadchar.js new file mode 100644 index 0000000000..f89c1d8b33 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-loadchar.js @@ -0,0 +1,44 @@ +// Load Latin-1 and Two-Byte strings. +function latin1AndTwoByte() { + for (var i = 0; i <= 0x03FF; ++i) { + var s = String.fromCodePoint(i); + assertEq(s[0], s); + assertEq(s.charAt(0), s); + } +} + +for (var i = 0; i < 2; ++i) { + latin1AndTwoByte(); +} + +// Test bi-morphic loads. +function stringAndArray() { + var xs = [["\u0100"], "\u0100"]; + for (var i = 0; i < 200; ++i) { + var x = xs[i & 1]; + var s = x[0]; + assertEq(s.length, 1); + assertEq(s, "\u0100"); + } +} + +for (var i = 0; i < 2; ++i) { + stringAndArray(); +} + +function outOfBoundsFailureThenException() { + var name = "Object"; + + var j = 0; + while (true) { + // Access out-of-bounds character and then trigger an exception, + // when accessing a property on the undefined value. + name[j++].does_not_exist; + } +} + +for (var i = 0; i < 2; ++i) { + try { + outOfBoundsFailureThenException(); + } catch {} +} diff --git a/js/src/jit-test/tests/cacheir/string-number-arith.js b/js/src/jit-test/tests/cacheir/string-number-arith.js new file mode 100644 index 0000000000..ec1caf76ff --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-number-arith.js @@ -0,0 +1,32 @@ +function test(half, minusOneHalf) { + assertEq(10 - half, 9.5); + assertEq(10 - minusOneHalf, 11.5); + assertEq(half - 0, 0.5); + assertEq(minusOneHalf - 1, -2.5); + + assertEq(10 * half, 5); + assertEq(half * 10, 5); + assertEq(10 * minusOneHalf, -15); + assertEq(minusOneHalf * 10, -15); + + assertEq(10 / half, 20); + assertEq(half / 1, 0.5); + assertEq(12 / minusOneHalf, -8); + assertEq(minusOneHalf / 1, -1.5); + + assertEq(10 % half, 0); + assertEq(half % 1, 0.5); + assertEq(12 % minusOneHalf, 0); + assertEq(minusOneHalf % 1, -0.5); + + assertEq(10 ** half, Math.sqrt(10)); + assertEq(half ** 4, 0.0625); + assertEq(16 ** minusOneHalf, 0.015625); + assertEq(minusOneHalf ** 4, 5.0625); +} + +for (var i = 0; i < 10; i++) { + test(0.5, -1.5); + test("0.5", "-1.5"); + test("5e-1", "-15e-1"); +} diff --git a/js/src/jit-test/tests/cacheir/string-toString-valueOf.js b/js/src/jit-test/tests/cacheir/string-toString-valueOf.js new file mode 100644 index 0000000000..af50c50256 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/string-toString-valueOf.js @@ -0,0 +1,23 @@ +function simple(str) { + assertEq(str.toString(), "abc"); + assertEq(str.valueOf(), "abc"); +} + +function obj(str) { + var obj = new String(str); + assertEq(obj.toString(), "xyz"); + assertEq(obj.valueOf(), "xyz"); +} + +function mixed() { + for (var v of ["abc", new String("abc")]) { + assertEq(v.toString(), "abc"); + assertEq(v.valueOf(), "abc"); + } +} + +for (var i = 0; i < 100; i++) { + simple("abc"); + obj("xyz"); + mixed(); +}
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/stub-fold-closeiter.js b/js/src/jit-test/tests/cacheir/stub-fold-closeiter.js new file mode 100644 index 0000000000..8e91d3737c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/stub-fold-closeiter.js @@ -0,0 +1,25 @@ +class Iterator { + val = 0; + next() { + return { value: this.val++, done: false } + } + return() { return { value: undefined, done: true }} +} + +var arr = []; +for (var i = 0; i < 10; i++) { + class SubIter extends Iterator {} + var iterable = { + [Symbol.iterator]() { return new SubIter(); } + } + arr.push(iterable); +} + +function foo() { + for (var x of arr[i % arr.length]) { + if (x > 1) { return; } + } +} + +with ({}) {} +for (var i = 0; i < 100; i++) { foo(); } diff --git a/js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js b/js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js new file mode 100644 index 0000000000..b1232c2e2c --- /dev/null +++ b/js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js @@ -0,0 +1,35 @@ +// Test loose equality comparison between Symbols and String/Boolean/Int32/Double/BigInt. + +var xs = [ + Symbol(), Symbol(), Symbol(), Symbol(), + Symbol(), Symbol(), Symbol(), Symbol(), +]; + +var ys = [ + "", "test", true, false, + 123, 123.5, NaN, 456n, +]; + +function testLooseEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + assertEq(x == y, false); + assertEq(y == x, false); + } +} +testLooseEqual(); + +function testLooseNotEqual() { + for (var i = 0; i < 100; ++i) { + var j = i % xs.length; + var x = xs[j]; + var y = ys[j]; + + assertEq(x != y, true); + assertEq(y != x, true); + } +} +testLooseNotEqual(); diff --git a/js/src/jit-test/tests/cacheir/tobool.js b/js/src/jit-test/tests/cacheir/tobool.js new file mode 100644 index 0000000000..8487703e1f --- /dev/null +++ b/js/src/jit-test/tests/cacheir/tobool.js @@ -0,0 +1,95 @@ +var success = 0; +var expected_bool = 0; +var expected_int = 0; +var expected_double = 0; +var expected_string = 0; +var expected_object = 0; +var expected_symbol = 0; + +function test_type_stable_ic() { + // Initialize as falsy where possible. + var a = undefined; // No Change, never succeeds + var b = null; // No Change, never succeeds + var c = false; // Alternate between true and false. + var d = 0; // Int32 cache checker, change int values + var e = ""; // String cache checker, change string values + var f = Symbol(); // No change, always succeed, no cache. + var g = {}; // Change objects, always succeed. + var h = -0; // Double cache checker, change double values. + + for (var i =0; i < 30; i++) { + // Switch between values to ensure the caches fire. + switch (i % 3) { + case 0: + d = 0; + e = "hi"; expected_string++; + c = true; expected_bool++; + h = 0; + break; + case 1: + d = 1; expected_int++; + e = ""; + c = false; + h = NaN; + break; + case 2: + d = 2; expected_int++; + h = 1.234; expected_double++; + g = {}; + break; + } + + if (a) { success++; } + if (b) { success++; } + if (c) { success++; } + if (d) { success++; } + if (e) { success++; } + if (f) { success++; } expected_symbol++; // Symbol succeed + if (g) { success++; } expected_object++; // Object success + if (h) { success++; } + } +} + +test_type_stable_ic(); + +assertEq(success, expected_bool + expected_double + expected_int + expected_object + expected_string + expected_symbol); + + +// Test cache failures. +function helper(fun, arg, n) +{ + var r = 0; + for (var i = 0; i < n; i++) { + r = fun(arg); + } + return r ? 1 : 0; +} + +function test_transition(fun, load, test, before, after) { + var a = helper(fun, load, 30); + var x = helper(fun, test, 5) + assertEq(a, before); + assertEq(x, after) +} + +var fun1 = (x) => { if (x) return true; else return false; }; +var fun2 = (x) => { if (x) return true; else return false; }; +var fun3 = (x) => { if (x) return true; else return false; }; +var fun4 = (x) => { if (x) return true; else return false; }; +var fun5 = (x) => { if (x) return true; else return false; }; +var fun6 = (x) => { if (x) return true; else return false; }; +var fun7 = (x) => { if (x) return true; else return false; }; +var fun8 = (x) => { if (x) return true; else return false; }; + +// NaN -> Int32 +test_transition(fun1, NaN, 1, 0, 1); +test_transition(fun2, 1, NaN, 1, 0); +// NaN -> Object / Object -> NaN +test_transition(fun3, NaN, {}, 0, 1); +test_transition(fun4, {}, NaN, 1, 0); +// Object -> null / null -> Object +test_transition(fun5, {}, null, 1, 0); +test_transition(fun6, null, {}, 0, 1); +// Symbol -> null, null -> Symbol +test_transition(fun7, Symbol('hi'), null, 1, 0); +test_transition(fun8, null, Symbol('lo'), 0, 1);
\ No newline at end of file diff --git a/js/src/jit-test/tests/cacheir/topropertykey.js b/js/src/jit-test/tests/cacheir/topropertykey.js new file mode 100644 index 0000000000..69f9e1be9d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/topropertykey.js @@ -0,0 +1,36 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +function testInt32() { + var xs = [0, 0]; + var a = [0]; + + for (var i = 0; i < 20; ++i) { + var key = xs[i & 1]; + assertEq(a[key]++, i); + } +} +for (var i = 0; i < 2; ++i) testInt32(); + +function testStringInt32() { + var xs = ["0", "0"]; + var a = [0]; + + for (var i = 0; i < 20; ++i) { + var key = xs[i & 1]; + assertEq(a[key]++, i); + } +} +for (var i = 0; i < 2; ++i) testStringInt32(); + +function testString() { + var xs = ["p", "p"]; + var a = { + p: 0, + }; + + for (var i = 0; i < 20; ++i) { + var key = xs[i & 1]; + assertEq(a[key]++, i); + } +} +for (var i = 0; i < 2; ++i) testString(); diff --git a/js/src/jit-test/tests/cacheir/typed-array-intrinsics.js b/js/src/jit-test/tests/cacheir/typed-array-intrinsics.js new file mode 100644 index 0000000000..788122bc79 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typed-array-intrinsics.js @@ -0,0 +1,93 @@ +var ab = new ArrayBuffer(128); +var uint8c = new Uint8ClampedArray(ab, 3); +var int16 = new Int16Array(ab, 2); +var int32 = new Int32Array(ab, 4); +var float32 = new Float32Array(ab, 0); +var float64 = new Float64Array(ab, 64); +var bigInt64 = new BigInt64Array(ab, 32); + +var uint8cProxy = new Proxy(uint8c, {}); +var g = newGlobal({newCompartment: true}); +var uint8cWrapped = g.evaluate("new Uint8ClampedArray(10)"); + +var TypedArrayElementSize = getSelfHostedValue("TypedArrayElementSize"); +var TypedArrayByteOffset = getSelfHostedValue("TypedArrayByteOffset"); +var IsTypedArray = getSelfHostedValue("IsTypedArray"); +var IsPossiblyWrappedTypedArray = getSelfHostedValue("IsPossiblyWrappedTypedArray"); +var TypedArrayLength = getSelfHostedValue("TypedArrayLength"); + +function testElementSize() { + function getSize(ta) { + return TypedArrayElementSize(ta); + } + assertEq(getSize(uint8c), 1); + assertEq(getSize(int16), 2); + assertEq(getSize(int32), 4); + assertEq(getSize(float32), 4); + assertEq(getSize(float64), 8); + assertEq(getSize(bigInt64), 8); +} + +function testByteOffset() { + function getOffset(ta) { + return TypedArrayByteOffset(ta); + } + assertEq(getOffset(uint8c), 3); + assertEq(getOffset(int16), 2); + assertEq(getOffset(int32), 4); + assertEq(getOffset(float32), 0); + assertEq(getOffset(float64), 64); + assertEq(getOffset(bigInt64), 32); +} + +function testIsTypedArray() { + function isTA(obj) { + return IsTypedArray(obj); + } + assertEq(isTA(uint8c), true); + assertEq(isTA(int16), true); + assertEq(isTA(int32), true); + assertEq(isTA(float32), true); + assertEq(isTA(float64), true); + assertEq(isTA(bigInt64), true); + assertEq(isTA(Math), false); + assertEq(isTA(ab), false); + assertEq(isTA(uint8cProxy), false); + assertEq(isTA(uint8cWrapped), false); +} + +function testIsPossiblyWrappedTypedArray() { + function isTA(obj) { + return IsPossiblyWrappedTypedArray(obj); + } + assertEq(isTA(uint8c), true); + assertEq(isTA(int16), true); + assertEq(isTA(int32), true); + assertEq(isTA(float32), true); + assertEq(isTA(float64), true); + assertEq(isTA(bigInt64), true); + assertEq(isTA(Math), false); + assertEq(isTA(ab), false); + assertEq(isTA(uint8cProxy), false); + assertEq(isTA(uint8cWrapped), true); +} + +function testTypedArrayLength() { + function getLength(obj) { + return TypedArrayLength(obj); + } + assertEq(getLength(uint8c), 125); + assertEq(getLength(int16), 63); + assertEq(getLength(int32), 31); + assertEq(getLength(float32), 32); + assertEq(getLength(float64), 8); + assertEq(getLength(bigInt64), 12); +} + +for (var i = 0; i < 40; i++) { + testElementSize(); + testByteOffset(); + testIsTypedArray(); + testIsPossiblyWrappedTypedArray(); + testTypedArrayLength(); +} diff --git a/js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js b/js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js new file mode 100644 index 0000000000..5733bc4c29 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js @@ -0,0 +1,60 @@ +function testArrayBufferThenOther() { + var buf = new ArrayBuffer(4); + var other = [1, 2]; + for (var i = 0; i < 150; i++) { + var arg = i < 100 ? buf : other; + var ta = new Int32Array(arg); + assertEq(ta.length, arg === buf ? 1 : 2); + } +} +testArrayBufferThenOther(); + +function testSharedArrayBufferThenOther() { + if (!this.SharedArrayBuffer) { + return; + } + var buf = new SharedArrayBuffer(4); + var other = [1, 2]; + for (var i = 0; i < 150; i++) { + var arg = i < 100 ? buf : other; + var ta = new Int32Array(arg); + assertEq(ta.length, arg === buf ? 1 : 2); + } +} +testSharedArrayBufferThenOther(); + +function testArrayThenArrayBuffer() { + var arr = [1, 2, 3]; + var buf = new ArrayBuffer(5); + for (var i = 0; i < 150; i++) { + var arg = i < 100 ? arr : buf; + var ta = new Int8Array(arg); + assertEq(ta.length, arg === arr ? 3 : 5); + } +} +testArrayThenArrayBuffer(); + +function testArrayThenSharedArrayBuffer() { + if (!this.SharedArrayBuffer) { + return; + } + var arr = [1, 2, 3]; + var buf = new SharedArrayBuffer(5); + for (var i = 0; i < 150; i++) { + var arg = i < 100 ? arr : buf; + var ta = new Int8Array(arg); + assertEq(ta.length, arg === arr ? 3 : 5); + } +} +testArrayThenSharedArrayBuffer(); + +function testArrayThenWrapper() { + var arr = [1, 2, 3]; + var wrapper = newGlobal({newCompartment: true}).evaluate("[1, 2]"); + for (var i = 0; i < 150; i++) { + var arg = i < 100 ? arr : wrapper; + var ta = new Int8Array(arg); + assertEq(ta.length, arg === arr ? 3 : 2); + } +} +testArrayThenWrapper(); diff --git a/js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js new file mode 100644 index 0000000000..e6404ae6e7 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js @@ -0,0 +1,53 @@ +function get(k) { + var p = {}; + p[k] = 1; + + // TypedArrays intercept any TypedArray indices (canonical numeric indices in the spec), + // so when reading |k| from |ta| we shouldn't return the inherited property |p[k]|. + var ta = new Int32Array(10); + Object.setPrototypeOf(ta, p); + + // Assume sixteen different objects trigger a transition to a megamorphic IC. + var xs = [ + ta, + + {a:0}, {b:0}, {c:0}, {d:0}, {e:0}, {f:0}, {g:0}, {h:0}, + {j:0}, {k:0}, {l:0}, {m:0}, {n:0}, {o:0}, {p:0}, + ]; + + for (var i = 0; i < 100; ++i) { + var x = xs[i & 15]; + assertEq(x[k], undefined); + } +} + +// Make sure we use a distinct function for each test. +function test(fn) { + return Function(`return ${fn};`)(); +} + +// TypedArray index representable as an Int32. +test(get)(100); +test(get)("100"); + +// TypedArray index not representable as an Int32. +test(get)(4294967296); +test(get)("4294967296"); + +// Non-finite TypedArray indices. +test(get)(Infinity); +test(get)("Infinity"); + +test(get)(-Infinity); +test(get)("-Infinity"); + +test(get)(NaN); +test(get)("NaN"); + +// TypedArray index with fractional parts. +test(get)(1.1); +test(get)("1.1"); + +// TypedArray index with exponent parts. +test(get)(1e+25); +test(get)("1e+25"); diff --git a/js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js new file mode 100644 index 0000000000..e2348caca9 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js @@ -0,0 +1,53 @@ +function has(k) { + var p = {}; + p[k] = 1; + + // TypedArrays intercept any TypedArray indices (canonical numeric indices in the spec), + // so when reading |k| from |ta| we shouldn't return the inherited property |p[k]|. + var ta = new Int32Array(10); + Object.setPrototypeOf(ta, p); + + // Assume sixteen different objects trigger a transition to a megamorphic IC. + var xs = [ + ta, + + {a:0}, {b:0}, {c:0}, {d:0}, {e:0}, {f:0}, {g:0}, {h:0}, + {j:0}, {k:0}, {l:0}, {m:0}, {n:0}, {o:0}, {p:0}, + ]; + + for (var i = 0; i < 100; ++i) { + var x = xs[i & 15]; + assertEq(k in x, false); + } +} + +// Make sure we use a distinct function for each test. +function test(fn) { + return Function(`return ${fn};`)(); +} + +// TypedArray index representable as an Int32. +test(has)(100); +test(has)("100"); + +// TypedArray index not representable as an Int32. +test(has)(4294967296); +test(has)("4294967296"); + +// Non-finite TypedArray indices. +test(has)(Infinity); +test(has)("Infinity"); + +test(has)(-Infinity); +test(has)("-Infinity"); + +test(has)(NaN); +test(has)("NaN"); + +// TypedArray index with fractional parts. +test(has)(1.1); +test(has)("1.1"); + +// TypedArray index with exponent parts. +test(has)(1e+25); +test(has)("1e+25"); diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js new file mode 100644 index 0000000000..5499c26152 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js @@ -0,0 +1,37 @@ +function get(k) { + // Different typed array types to ensure we emit a GetProp IC. + var xs = [ + new Int32Array(10), + new Int16Array(10), + ]; + + // Doesn't read values from the prototype chain. + Object.prototype[k] = 1; + + for (var i = 0; i < 100; ++i) { + var x = xs[i & 1]; + assertEq(x[k], undefined); + } +} + +// Make sure we use a distinct function for each test. +function test(fn) { + return Function(`return ${fn};`)(); +} + +// Negative numbers values aren't int32 indices. +test(get)(-1); +test(get)(-2147483648); +test(get)(-2147483649); + +// Int32 indices must be less-or-equal to max-int32. +test(get)(2147483648); + +// Int32 indices must not have fractional parts. +test(get)(1.5); +test(get)(-1.5); + +// Non-finite numbers aren't int32 indices. +test(get)(NaN); +test(get)(-Infinity); +test(get)(Infinity); diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js new file mode 100644 index 0000000000..47eda321ad --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js @@ -0,0 +1,37 @@ +function has(k) { + // Different typed array types to ensure we emit a HasProp IC. + var xs = [ + new Int32Array(10), + new Int16Array(10), + ]; + + // Doesn't read values from the prototype chain. + Object.prototype[k] = 1; + + for (var i = 0; i < 100; ++i) { + var x = xs[i & 1]; + assertEq(k in x, false); + } +} + +// Make sure we use a distinct function for each test. +function test(fn) { + return Function(`return ${fn};`)(); +} + +// Negative numbers values aren't int32 indices. +test(has)(-1); +test(has)(-2147483648); +test(has)(-2147483649); + +// Int32 indices must be less-or-equal to max-int32. +test(has)(2147483648); + +// Int32 indices must not have fractional parts. +test(has)(1.5); +test(has)(-1.5); + +// Non-finite numbers aren't int32 indices. +test(has)(NaN); +test(has)(-Infinity); +test(has)(Infinity); diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js new file mode 100644 index 0000000000..c3a6637516 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js @@ -0,0 +1,37 @@ +function set(k) { + // Different typed array types to ensure we emit a SetProp IC. + var xs = [ + new Int32Array(10), + new Int16Array(10), + ]; + + for (var i = 0; i < 100; ++i) { + var x = xs[i & 1]; + x[k] = 0; + } + + assertEq(xs[0][k], undefined); + assertEq(xs[1][k], undefined); +} + +// Make sure we use a distinct function for each test. +function test(fn) { + return Function(`return ${fn};`)(); +} + +// Negative numbers values aren't int32 indices. +test(set)(-1); +test(set)(-2147483648); +test(set)(-2147483649); + +// Int32 indices must be less-or-equal to max-int32. +test(set)(2147483648); + +// Int32 indices must not have fractional parts. +test(set)(1.5); +test(set)(-1.5); + +// Non-finite numbers aren't int32 indices. +test(set)(NaN); +test(set)(-Infinity); +test(set)(Infinity); diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js b/js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js new file mode 100644 index 0000000000..f0ca0c6ac8 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js @@ -0,0 +1,126 @@ +const types = [ + "Int8", + "Int16", + "Int32", + "Uint8", + "Uint16", + "Uint32", + "Uint8Clamped", + "Float32", + "Float64", +]; + +function convert(type, value) { + let num = Number(value); + switch (type) { + case "Int8": + return ((num | 0) << 24) >> 24; + case "Int16": + return ((num | 0) << 16) >> 16; + case "Int32": + return (num | 0); + case "Uint8": + return (num >>> 0) & 0xff; + case "Uint16": + return (num >>> 0) & 0xffff; + case "Uint32": + return (num >>> 0); + case "Uint8Clamped": { + if (Number.isNaN(num)) { + return 0; + } + let clamped = Math.max(0, Math.min(num, 255)); + let f = Math.floor(clamped); + if (clamped < f + 0.5) { + return f; + } + if (clamped > f + 0.5) { + return f + 1; + } + return f + (f & 1); + } + case "Float32": + return Math.fround(num); + case "Float64": + return num; + } + throw new Error(); +} + + +function runTest(type, initial, values) { + let expected = values.map(v => convert(type, v)); + assertEq( + expected.some(e => Object.is(e, initial)), + false, + "initial must be different from the expected values" + ); + + // Create a fresh function to ensure ICs are specific to a single TypedArray kind. + let test = Function("initial, values, expected", ` + let ta = new ${type}Array(1); + for (let i = 0; i < 200; ++i) { + ta[0] = initial; + ta[0] = values[i % values.length]; + assertEq(ta[0], expected[i % expected.length]); + } + `); + test(initial, values, expected); +} + +const tests = [ + // |null| is coerced to zero. + { + initial: 1, + values: [null], + }, + + // |undefined| is coerced to zero or NaN. + { + initial: 1, + values: [undefined], + }, + + // |false| is coerced to zero and |true| is coerced to one. + { + initial: 2, + values: [false, true], + }, + + // Strings without a fractional part. + { + initial: 42, + values: [ + "0", "1", "10", "111", "128", "256", "0x7fffffff", "0xffffffff", + ], + }, + + // Strings without a fractional part, but a leading minus sign. + { + initial: 42, + values: [ + "-0", "-1", "-10", "-111", "-128", "-256", "-2147483647", "-4294967295", + ], + }, + + // Strings with a fractional number part. + { + initial: 42, + values: [ + "0.1", "1.2", "10.8", "111.9", + "-0.1", "-1.2", "-10.8", "-111.9", + ], + }, + + // Special values and strings not parseable as a number. + { + initial: 42, + values: ["Infinity", "-Infinity", "NaN", "foobar"], + }, +]; + +for (let type of types) { + for (let {initial, values} of tests) { + runTest(type, initial, values); + } +} diff --git a/js/src/jit-test/tests/cacheir/typeof-proxy.js b/js/src/jit-test/tests/cacheir/typeof-proxy.js new file mode 100644 index 0000000000..0f97da9e2d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/typeof-proxy.js @@ -0,0 +1,19 @@ +function test() { + var funs = [function() {}, new Proxy(function() {}, {}), wrapWithProto(function() {}, null), new Proxy(createIsHTMLDDA(), {})]; + var objects = [{}, new Proxy({}, {}), wrapWithProto({}, null)]; + var undefs = [createIsHTMLDDA(), wrapWithProto(createIsHTMLDDA(), null)]; + + for (var fun of funs) { + assertEq(typeof fun, "function") + } + + for (var obj of objects) { + assertEq(typeof obj, "object"); + } + + for (var undef of undefs) { + assertEq(typeof undef, "undefined"); + } +} + +test(); diff --git a/js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js b/js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js new file mode 100644 index 0000000000..cb4894ea89 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js @@ -0,0 +1,26 @@ +function test(input, resPos, resNeg, resToNumeric, resInc, resDec) { + assertEq(+input, resPos); + assertEq(-input, resNeg); + + var input1 = input; + assertEq(input1++, resToNumeric); + assertEq(input1, resInc); + + var input2 = input; + assertEq(++input2, resInc); + assertEq(input1, resInc); + + var input3 = input; + assertEq(input3--, resToNumeric); + assertEq(input3, resDec); + + var input4 = input; + assertEq(--input4, resDec); + assertEq(input4, resDec); +} +for (var i = 0; i < 50; i++) { + test(null, 0, -0, 0, 1, -1); + test(undefined, NaN, NaN, NaN, NaN, NaN); + test(true, 1, -1, 1, 2, 0); + test(false, 0, -0, 0, 1, -1); +} diff --git a/js/src/jit-test/tests/cacheir/unaryarith-string.js b/js/src/jit-test/tests/cacheir/unaryarith-string.js new file mode 100644 index 0000000000..853d8f7464 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/unaryarith-string.js @@ -0,0 +1,58 @@ +setJitCompilerOption('ion.forceinlineCaches', 1); + +function warmup(fun, input_array, output_array) { + assertEq(output_array.length, input_array.length); + for (var index = 0; index < input_array.length; index++) { + input = input_array[index]; + output = output_array[index]; + for (var i = 0; i < 30; i++) { + var y = fun(input); + assertEq(y, output) + } + } +} + +// Unary - operator with transition between string representations of int32 and double. +var fun_neg_int32_double = (x) => { return -x; } +var fun_neg_double_int32 = (x) => { return -x; } + +// Unary ~ operator using string representations of either int32 or double. +var fun_bitnot_int32 = (x) => { return ~x; } +var fun_bitnot_double = (x) => { return ~x; } + +// Unary ++ operator using string representations of either int32 or double. +var fun_inc_int32 = (x) => { return ++x; } +var fun_inc_double = (x) => { return ++x; } + +// Unary -- operator using string representations of either int32 or double. +var fun_dec_int32 = (x) => { return --x; } +var fun_dec_double = (x) => { return --x; } + +// Unary + operator using string representations of either int32 or double. +var fun_pos_int32 = (x) => { return +x; } +var fun_pos_double = (x) => { return +x; } + +// JSOp::ToNumeric using string representations of either int32 or double. +var fun_tonumeric_int32 = (x) => { return x++; } +var fun_tonumeric_double = (x) => { return x++; } + +warmup(fun_neg_int32_double, ["1", "2", "-3"], [-1, -2, 3]); +warmup(fun_neg_double_int32, ["0"], [-0]); + +warmup(fun_neg_double_int32, ["3", "4", "-5"], [-3, -4, 5]); +warmup(fun_neg_int32_double, ["1.2", "1.4"], [-1.2, -1.4]); + +warmup(fun_bitnot_int32, ["-1", "0"], [0, -1]); +warmup(fun_bitnot_double, ["-1.0", "0.0", "1.2", "3"], [0, -1, -2, -4]); + +warmup(fun_inc_int32, ["-1", "0"], [0, 1]); +warmup(fun_inc_double, ["-1.0", "0.0", "1.2", "3"], [0, 1, 2.2, 4]); + +warmup(fun_dec_int32, ["-1", "0"], [-2, -1]); +warmup(fun_dec_double, ["-1.0", "0.0", "1.5", "3"], [-2, -1, 0.5, 2]); + +warmup(fun_pos_int32, ["-1", "0"], [-1, 0]); +warmup(fun_pos_double, ["-1.0", "0.0", "1.2", "3"], [-1.0, 0.0, 1.2, 3]); + +warmup(fun_tonumeric_int32, ["-1", "0"], [-1, 0]); +warmup(fun_tonumeric_double, ["-1.0", "0.0", "1.2", "3"], [-1.0, 0.0, 1.2, 3]); diff --git a/js/src/jit-test/tests/cacheir/unaryarith.js b/js/src/jit-test/tests/cacheir/unaryarith.js new file mode 100644 index 0000000000..39dda5b0ee --- /dev/null +++ b/js/src/jit-test/tests/cacheir/unaryarith.js @@ -0,0 +1,62 @@ +setJitCompilerOption('ion.forceinlineCaches', 1); + +function warmup(fun, input_array, output_array) { + assertEq(output_array.length, input_array.length); + for (var index = 0; index < input_array.length; index++) { + input = input_array[index]; + output = output_array[index]; + for (var i = 0; i < 30; i++) { + var y = fun(input); + assertEq(y, output) + } + } +} + +// Unary - operator with transition between int32 and double. +var fun_neg_int32_double = (x) => { return -x; } +var fun_neg_double_int32 = (x) => { return -x; } + +// Unary ~ operator using either int32, double, null, or undefined. +var fun_bitnot_int32 = (x) => { return ~x; } +var fun_bitnot_double = (x) => { return ~x; } +var fun_bitnot_null = (x) => { return ~x; } +var fun_bitnot_undefined = (x) => { return ~x; } + +// Unary ++ operator using either int32 or double. +var fun_inc_int32 = (x) => { return ++x; } +var fun_inc_double = (x) => { return ++x; } + +// Unary -- operator using either int32 or double. +var fun_dec_int32 = (x) => { return --x; } +var fun_dec_double = (x) => { return --x; } + +// Unary + operator using either int32 or double. +var fun_pos_int32 = (x) => { return +x; } +var fun_pos_double = (x) => { return +x; } + +// JSOp::ToNumeric using either int32 or double. +var fun_tonumeric_int32 = (x) => { return x++; } +var fun_tonumeric_double = (x) => { return x++; } + +warmup(fun_neg_int32_double, [1, 2], [-1, -2]); +warmup(fun_neg_double_int32, [0], [-0]); + +warmup(fun_neg_double_int32, [3, 4], [-3, -4]); +warmup(fun_neg_int32_double, [1.2, 1.4], [-1.2, -1.4]); + +warmup(fun_bitnot_int32, [-1, 0], [0, -1]); +warmup(fun_bitnot_double, [-1.0, 0.0, 1.2, 3], [0, -1, -2, -4]); +warmup(fun_bitnot_null, [null], [-1]); +warmup(fun_bitnot_undefined, [void 0], [-1]); + +warmup(fun_inc_int32, [-1, 0], [0, 1]); +warmup(fun_inc_double, [-1.0, 0.0, 1.2, 3], [0, 1, 2.2, 4]); + +warmup(fun_dec_int32, [-1, 0], [-2, -1]); +warmup(fun_dec_double, [-1.0, 0.0, 1.5, 3], [-2, -1, 0.5, 2]); + +warmup(fun_pos_int32, [-1, 0], [-1, 0]); +warmup(fun_pos_double, [-1.0, 0.0, 1.2, 3], [-1.0, 0.0, 1.2, 3]); + +warmup(fun_tonumeric_int32, [-1, 0], [-1, 0]); +warmup(fun_tonumeric_double, [-1.0, 0.0, 1.2, 3], [-1.0, 0.0, 1.2, 3]); diff --git a/js/src/jit-test/tests/cacheir/unboxed-element-hole.js b/js/src/jit-test/tests/cacheir/unboxed-element-hole.js new file mode 100644 index 0000000000..c3afd13dab --- /dev/null +++ b/js/src/jit-test/tests/cacheir/unboxed-element-hole.js @@ -0,0 +1,41 @@ +function noElement() { + for (var i = 0; i < 1e4; i++) { + var obj = {length: 0}; + assertEq(obj[0], undefined); + } +} + +function noElementCheckPrototype() { + for (var i = 0; i < 1e4; i++) { + var obj = {length: 0}; + assertEq(obj[0], i <= 1e3 ? undefined : 1); + if (i == 1e3) { + Object.prototype[0] = 1; + } + } + delete Object.prototype[0]; +} + +function elementOnPrototype() { + Object.prototype[0] = 3; + for (var i = 0; i < 1e4; i++) { + var obj = {length: 0}; + assertEq(obj[0], 3); + } + delete Object.prototype[0]; +} + +function checkExpando() { + for (var i = 0; i < 1e4; i++) { + var obj = {length: 0}; + if (i >= 1e3) { + obj[0] = 2; + } + assertEq(obj[0], i < 1e3 ? undefined : 2); + } +} + +noElement(); +noElementCheckPrototype(); +elementOnPrototype(); +checkExpando(); diff --git a/js/src/jit-test/tests/cacheir/windowproxy.js b/js/src/jit-test/tests/cacheir/windowproxy.js new file mode 100644 index 0000000000..994af384e4 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/windowproxy.js @@ -0,0 +1,32 @@ + +var g = newGlobal({ + sameZoneAs: this, + useWindowProxy: true, +}); + +g.evaluate(` + this.data = 7; + + // Getter / Setter + Object.defineProperty(this, "prop", { + get: function() { return this.data; }, + set: function(val) { this.data = val; }, + }); + + // Getter / Setter ICs + for (var i = 0; i < 20; ++i) { + this.data = i; + assertEq(prop, i); + prop = i; + assertEq(this.prop, i); + this.prop = i; + assertEq(this.data, i); + } +`); + + +// CCW of WindowProxy +for (var i = 0; i < 20; ++i) { + g.slot = i; + assertEq(g.slot, i); +} |