diff options
Diffstat (limited to 'js/src/jit-test/tests/warp')
64 files changed, 1791 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/warp/arguments-object-load-arg.js b/js/src/jit-test/tests/warp/arguments-object-load-arg.js new file mode 100644 index 0000000000..0598c3b1d6 --- /dev/null +++ b/js/src/jit-test/tests/warp/arguments-object-load-arg.js @@ -0,0 +1,109 @@ +// Test transpiling of LoadArgumentsObjectArgResult and cover all possible bailout conditions. + +function blackhole() { + // Direct eval prevents any compile-time optimisations. + eval(""); +} + +function testConstantArgAccess() { + blackhole(arguments); // Create an arguments object. + + for (var i = 0; i < 50; ++i) { + assertEq(arguments[0], 1); + } +} +for (var i = 0; i < 20; ++i) testConstantArgAccess(1); + +function testDynamicArgAccess() { + blackhole(arguments); // Create an arguments object. + + for (var i = 0; i < 50; ++i) { + assertEq(arguments[i & 1], 1 + (i & 1)); + } +} +for (var i = 0; i < 20; ++i) testDynamicArgAccess(1, 2); + +function markElementOveriddenIf(args, cond, value) { + with ({}) ; // Don't Warp compile to avoid cold code bailouts. + if (cond) { + Object.defineProperty(args, 0, {value}); + } +} + +function testBailoutElementReified() { + blackhole(arguments); // Create an arguments object. + + for (var i = 0; i < 50; ++i) { + markElementOveriddenIf(arguments, i === 25, 2); + + var expected = 1 + (i >= 25); + assertEq(arguments[0], expected); + } +} +for (var i = 0; i < 20; ++i) testBailoutElementReified(1); + +function markLengthOveriddenIf(args, cond, value) { + with ({}) ; // Don't Warp compile to avoid cold code bailouts. + if (cond) { + args.length = value; + } +} + +function testBailoutLengthReified() { + blackhole(arguments); // Create an arguments object. + + for (var i = 0; i < 50; ++i) { + markLengthOveriddenIf(arguments, i === 25, 0); + + assertEq(arguments[0], 1); + } +} +for (var i = 0; i < 20; ++i) testBailoutLengthReified(1); + +function deleteElementIf(args, cond) { + with ({}) ; // Don't Warp compile to avoid cold code bailouts. + if (cond) { + delete args[0]; + } +} + +function testBailoutElementDeleted() { + blackhole(arguments); // Create an arguments object. + + // Load expected values from an array to avoid possible cold code bailouts. + var values = [1, undefined]; + + for (var i = 0; i < 50; ++i) { + deleteElementIf(arguments, i === 25); + + var expected = values[0 + (i >= 25)]; + assertEq(arguments[0], expected); + } +} +for (var i = 0; i < 20; ++i) testBailoutElementDeleted(1); + +function testBailoutOutOfBounds() { + blackhole(arguments); // Create an arguments object. + + // Load expected values from an array to avoid possible cold code bailouts. + var values = [1, undefined]; + + for (var i = 0; i < 50; ++i) { + var index = 0 + (i >= 25); + var expected = values[index]; + assertEq(arguments[index], expected); + } +} +for (var i = 0; i < 20; ++i) testBailoutOutOfBounds(1); + +function testBailoutArgForwarded(arg1, arg2) { + blackhole(arguments); // Create an arguments object. + blackhole(() => arg2); // Ensure |arg2| is marked as "forwarded". + + for (var i = 0; i < 50; ++i) { + var index = 0 + (i >= 25); + var expected = 1 + (i >= 25); + assertEq(arguments[index], expected); + } +} +for (var i = 0; i < 20; ++i) testBailoutArgForwarded(1, 2); diff --git a/js/src/jit-test/tests/warp/arguments-object-load-length.js b/js/src/jit-test/tests/warp/arguments-object-load-length.js new file mode 100644 index 0000000000..935c406ac0 --- /dev/null +++ b/js/src/jit-test/tests/warp/arguments-object-load-length.js @@ -0,0 +1,57 @@ +// Test transpiling of LoadArgumentsObjectLengthResult and cover all possible bailout conditions. + +function blackhole() { + // Direct eval prevents any compile-time optimisations. + eval(""); +} + +function testLengthAccess() { + blackhole(arguments); // Create an arguments object. + + for (var i = 0; i < 50; ++i) { + assertEq(arguments.length, 1); + } +} +for (var i = 0; i < 20; ++i) testLengthAccess(1); + +function markLengthOveriddenIf(args, cond, value) { + with ({}) ; // Don't Warp compile to avoid cold code bailouts. + if (cond) { + args.length = value; + } +} + +function testBailoutLengthReified() { + blackhole(arguments); // Create an arguments object. + + for (var i = 0; i < 50; ++i) { + markLengthOveriddenIf(arguments, i === 25, 0); + + var expected = 0 + (i < 25); + assertEq(arguments.length, expected); + } +} +for (var i = 0; i < 20; ++i) testBailoutLengthReified(1); + + +function deleteLengthIf(args, cond) { + with ({}) ; // Don't Warp compile to avoid cold code bailouts. + if (cond) { + delete args.length; + } +} + +function testBailoutLengthDeleted() { + blackhole(arguments); // Create an arguments object. + + // Load expected values from an array to avoid possible cold code bailouts. + var values = [1, undefined]; + + for (var i = 0; i < 50; ++i) { + deleteLengthIf(arguments, i === 25); + + var expected = values[0 + (i >= 25)]; + assertEq(arguments.length, expected); + } +} +for (var i = 0; i < 20; ++i) testBailoutLengthDeleted(1); diff --git a/js/src/jit-test/tests/warp/booleantostring.js b/js/src/jit-test/tests/warp/booleantostring.js new file mode 100644 index 0000000000..39ff0b1b6d --- /dev/null +++ b/js/src/jit-test/tests/warp/booleantostring.js @@ -0,0 +1,9 @@ +var a = [true, false]; +for (var i = 0; i < 1e4; i++) { + var str = "x: " + a[i & 1]; + if (i & 1) { + assertEq(str, "x: false"); + } else { + assertEq(str, "x: true"); + } +} diff --git a/js/src/jit-test/tests/warp/bug1646041.js b/js/src/jit-test/tests/warp/bug1646041.js new file mode 100644 index 0000000000..892553ed9f --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1646041.js @@ -0,0 +1,9 @@ +// |jit-test| --ion-warmup-threshold=2 +function f() { + while (true) { + return 1; + } +} +for (var i = 0; i < 100; i++) { + f(); +} diff --git a/js/src/jit-test/tests/warp/bug1646302.js b/js/src/jit-test/tests/warp/bug1646302.js new file mode 100644 index 0000000000..bf86fe702c --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1646302.js @@ -0,0 +1,9 @@ +function f(x) { + function fnc() {} + fnc.prototype = 3; + new fnc; + if (x < 50) { + new new.target(x + 1); + } +} +new f(0); diff --git a/js/src/jit-test/tests/warp/bug1647054.js b/js/src/jit-test/tests/warp/bug1647054.js new file mode 100644 index 0000000000..fa868c60a8 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1647054.js @@ -0,0 +1,8 @@ +function f() { + for (var i = 0; i < 200; ++i) { + for (var j = 0; 0 & ++i; ++j) { + i(); + } + } +} +f(); diff --git a/js/src/jit-test/tests/warp/bug1652049.js b/js/src/jit-test/tests/warp/bug1652049.js new file mode 100644 index 0000000000..a28e35d353 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1652049.js @@ -0,0 +1,7 @@ +function f() { + var o = {__proto__: null}; + for (var i = 0; i < 15; i++) { + assertEq("foo" in o, false); + } +} +f(); diff --git a/js/src/jit-test/tests/warp/bug1652732.js b/js/src/jit-test/tests/warp/bug1652732.js new file mode 100644 index 0000000000..f5df3bc648 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1652732.js @@ -0,0 +1,8 @@ +function test() { + var obj = {}; + obj[{}] = 1; + f = () => { for (var x of obj) {} }; +} +for (var i = 0; i < 5; i++) { + test(); +} diff --git a/js/src/jit-test/tests/warp/bug1653913.js b/js/src/jit-test/tests/warp/bug1653913.js new file mode 100644 index 0000000000..adaaf3c7c8 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1653913.js @@ -0,0 +1,3 @@ +var s = "aaaaaaaaaaaa"; +var a = [, [...s]]; +assertEq(a.toString(), ",a,a,a,a,a,a,a,a,a,a,a,a"); diff --git a/js/src/jit-test/tests/warp/bug1653972.js b/js/src/jit-test/tests/warp/bug1653972.js new file mode 100644 index 0000000000..953bb0077f --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1653972.js @@ -0,0 +1,11 @@ +function maybeSetLength(arr, b) { + if (b) { + arr.length = 0x8000_1111; + } +} +var arr = []; +for (var i = 0; i < 1600; i++) { + maybeSetLength(arr, i > 1500); + arr.push(2); +} +assertEq(arr.length, 0x8000_1112); diff --git a/js/src/jit-test/tests/warp/bug1661530.js b/js/src/jit-test/tests/warp/bug1661530.js new file mode 100644 index 0000000000..c4ea7fdb92 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1661530.js @@ -0,0 +1,7 @@ +Function.prototype.call = function() {}; +var sum = 0; +function foo() { sum++; } +for (var i = 0; i < 1000; i++) { + foo.call({}, 0); +} +assertEq(sum, 0); diff --git a/js/src/jit-test/tests/warp/bug1661728.js b/js/src/jit-test/tests/warp/bug1661728.js new file mode 100644 index 0000000000..ac4ad86552 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1661728.js @@ -0,0 +1,43 @@ +with ({}) {} // Don't inline anything into the top-level script. + +function foo() {} + +function inline_foo_into_bar() { + with ({}) {} // Don't inline anything into this function. + for (var i = 0; i < 10; i++) { + bar(2); + } + +} + +function bar(x) { + switch (x) { + case 1: + inline_foo_into_bar(); + + // Trigger a compacting gc to discard foo's jitscript. + // Do it while bar is on the stack to keep bar's jitscript alive. + gc(foo, 'shrinking'); + break; + case 2: + foo(); + break; + case 3: + break; + } +} + +// Warm up foo and bar. +for (var i = 0; i < 10; i++) { + foo(); + bar(3); +} + +// Inline and discard foo's jitscript. +bar(1); + +// Warp-compile bar +for (var i = 0; i < 50; i++) { + foo(); // ensure that foo has a new jitscript + bar(3); +} diff --git a/js/src/jit-test/tests/warp/bug1662146.js b/js/src/jit-test/tests/warp/bug1662146.js new file mode 100644 index 0000000000..cfc62b98f2 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1662146.js @@ -0,0 +1,12 @@ +var sum = 0; +function f1(a) { + try { + not_a_function(); + } catch (e) { + sum++; + } +} +for (var i = 0; i < 50; ++i) { + f1.call(); +} +assertEq(sum, 50); diff --git a/js/src/jit-test/tests/warp/bug1663993.js b/js/src/jit-test/tests/warp/bug1663993.js new file mode 100644 index 0000000000..c7199e85cf --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1663993.js @@ -0,0 +1,9 @@ +function r(relazify) { + "foo".substr(0); + if (relazify) relazifyFunctions(); +} + +for (var i = 0; i < 10; i++) { + r(i == 9); + r(""); +} diff --git a/js/src/jit-test/tests/warp/bug1664007.js b/js/src/jit-test/tests/warp/bug1664007.js new file mode 100644 index 0000000000..de5d8d1233 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1664007.js @@ -0,0 +1,12 @@ +function f(depth) { + function Obj() { + this.prop = null; + this.prop = this; + } + var o = new Obj(); + assertEq(o.prop, o); + if (depth < 1000) { + f(depth + 1); + } +} +f(0); diff --git a/js/src/jit-test/tests/warp/bug1665303.js b/js/src/jit-test/tests/warp/bug1665303.js new file mode 100644 index 0000000000..b2c26e01cf --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1665303.js @@ -0,0 +1,20 @@ +// |jit-test| skip-if: !('oomTest' in this); --fast-warmup + +// Prevent slowness with --ion-eager. +setJitCompilerOption("ion.warmup.trigger", 100); + +function f() { return 1; } +function test() { + oomTest(function() { + function foo() { + for (var i = 0; i < 10; i++) { + f(); + trialInline(); + } + } + evaluate(foo.toString() + "foo()"); + }); +} +for (var i = 0; i < 3; i++) { + test(); +} diff --git a/js/src/jit-test/tests/warp/bug1666070.js b/js/src/jit-test/tests/warp/bug1666070.js new file mode 100644 index 0000000000..54f737689a --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1666070.js @@ -0,0 +1,8 @@ +// |jit-test| --fast-warmup +function f() {} +for (var i = 0; i < 15; i++) { + f(); + var g = newGlobal(); + g.trialInline(); +} +trialInline(); diff --git a/js/src/jit-test/tests/warp/bug1666142-1.js b/js/src/jit-test/tests/warp/bug1666142-1.js new file mode 100644 index 0000000000..b5bb0aca77 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1666142-1.js @@ -0,0 +1,19 @@ +// |jit-test| --fast-warmup + +// This test triggers a GC in CreateThisForIC, +// without using the arguments rectifier. +var records = []; +function Record() { + return Object.create(null); +} +function init() { + records.push(new Record()); +} +function f() { + for (var i = 0; i < 100; i++) { + init(); + } +} + +gczeal(14,25); +f(); diff --git a/js/src/jit-test/tests/warp/bug1666142-2.js b/js/src/jit-test/tests/warp/bug1666142-2.js new file mode 100644 index 0000000000..9fa94e8f20 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1666142-2.js @@ -0,0 +1,19 @@ +// |jit-test| --fast-warmup + +// This test triggers a GC in CreateThisForIC, +// while using the arguments rectifier. +var records = []; +function Record(val) { + return Object.create(null); +} +function init() { + records.push(new Record()); +} +function f() { + for (var i = 0; i < 100; i++) { + init(); + } +} + +gczeal(14,25); +f(); diff --git a/js/src/jit-test/tests/warp/bug1667680.js b/js/src/jit-test/tests/warp/bug1667680.js new file mode 100644 index 0000000000..8e1b8c0097 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1667680.js @@ -0,0 +1,8 @@ +// |jit-test| --ion-limit-script-size=off +function f() { + var s = "for (var i = 0; i < 100; i++) {}; return 2;"; + s += "var x = [" + "9,".repeat(100_000) + "];"; + var g = Function(s); + assertEq(g(), 2); +} +f(); diff --git a/js/src/jit-test/tests/warp/bug1667685.js b/js/src/jit-test/tests/warp/bug1667685.js new file mode 100644 index 0000000000..2b9e392d24 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1667685.js @@ -0,0 +1,27 @@ +// |jit-test| skip-if: !('oomTest' in this); --fast-warmup + +// Prevent slowness with --ion-eager. +setJitCompilerOption("ion.warmup.trigger", 100); + +function h() { + return 1; +} +function g() { + for (var j = 0; j < 10; j++) { + h(); + } + trialInline(); +} +function f() { + for (var i = 0; i < 2; i++) { + var fun = Function(g.toString() + "g()"); + try { + fun(); + } catch {} + try { + fun(); + } catch {} + } + +} +oomTest(f); diff --git a/js/src/jit-test/tests/warp/bug1667699.js b/js/src/jit-test/tests/warp/bug1667699.js new file mode 100644 index 0000000000..a88115c7c5 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1667699.js @@ -0,0 +1,15 @@ +// |jit-test| --fast-warmup +function f(s) { + // Trial-inline self-hosted |replace| and relazify. + for (var i = 0; i < 50; i++) { + s = s.replace("a", "b"); + } + trialInline(); + relazifyFunctions(); + + // Warp-compile. + for (var j = 0; j < 50; j++) {} + + return s; +} +assertEq(f("a"), "b"); diff --git a/js/src/jit-test/tests/warp/bug1668197.js b/js/src/jit-test/tests/warp/bug1668197.js new file mode 100644 index 0000000000..2dcd6cb376 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1668197.js @@ -0,0 +1,6 @@ +// |jit-test| skip-if: !('oomTest' in this) +function f(x, y) { + return ~Math.hypot(x >>> 0, 2 - x >>> 0); +} +f(2, Math); +oomTest(f); diff --git a/js/src/jit-test/tests/warp/bug1669415.js b/js/src/jit-test/tests/warp/bug1669415.js new file mode 100644 index 0000000000..e22497f86d --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1669415.js @@ -0,0 +1,11 @@ +function f(x, y) { + return +(-y ? -x : (y ? x : NaN)); +} +let arr = [false, {}, {}]; +for (let i = 0; i < 9; ++i) { + f(1.1, 2); +} +for (let i = 0; i < arr.length; i++) { + output = f(true, arr[i]); +} +assertEq(output, 1); diff --git a/js/src/jit-test/tests/warp/bug1669597.js b/js/src/jit-test/tests/warp/bug1669597.js new file mode 100644 index 0000000000..c4110c581f --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1669597.js @@ -0,0 +1,14 @@ +// |jit-test| --fast-warmup +var str = ''; +function g(x) { + with(this) {} // Don't inline. + return x; +} +function f() { + var x = 0; + for (var i = 0; i < 100; i++) { + x += +g(+str); + } + return x; +} +assertEq(f(), 0); diff --git a/js/src/jit-test/tests/warp/bug1671812.js b/js/src/jit-test/tests/warp/bug1671812.js new file mode 100644 index 0000000000..560a01a871 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1671812.js @@ -0,0 +1,11 @@ +// |jit-test| --fast-warmup; --baseline-eager +function f() { + let val1 = Math.sqrt(9007199254740992); + let val2 = 0; + let arr = new Float32Array(100); + for (let i = 0; i < 100; i++) { + val2 = arr[i]; + } + return val1 + val2; +} +assertEq(f(), Math.sqrt(9007199254740992)); diff --git a/js/src/jit-test/tests/warp/bug1676631.js b/js/src/jit-test/tests/warp/bug1676631.js new file mode 100644 index 0000000000..d9f7947396 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1676631.js @@ -0,0 +1,7 @@ +function f() { + var a = arguments; + for (var i = 0; i < 10; i++) { + a[""] + } +} +f(); diff --git a/js/src/jit-test/tests/warp/bug1676639.js b/js/src/jit-test/tests/warp/bug1676639.js new file mode 100644 index 0000000000..f813c53a9a --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1676639.js @@ -0,0 +1,7 @@ +function foo() { + return Math.atanh(true === Math.fround(0) | 0) != true; +} +var results = []; +for (var j = 0; j < 50; j++) { + results.push(foo(0,0)); +} diff --git a/js/src/jit-test/tests/warp/bug1681056.js b/js/src/jit-test/tests/warp/bug1681056.js new file mode 100644 index 0000000000..6a3d094854 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1681056.js @@ -0,0 +1,9 @@ +gczeal(14,10); +let y = []; +try { + evaluate(`(function() { + for (let x10 = 0; + new class Object extends Object { v = function () {} }; + arguments << this) {} + })()`); +} catch(exc) {} diff --git a/js/src/jit-test/tests/warp/bug1681597.js b/js/src/jit-test/tests/warp/bug1681597.js new file mode 100644 index 0000000000..cc0d83f4b9 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1681597.js @@ -0,0 +1,19 @@ +// |jit-test| --fast-warmup; --no-threads + +function f(x) { + (function () { + (1 == (x & 0) * 1.1) + x; + })(); +} +let y = [,,,2147483648,0,0]; +for (let i = 0; i < 6; i++) { + f(y[i]); + f(y[i]); + f(y[i]); + f(y[i]); + f(y[i]); + f(y[i]); + f(y[i]); + f(y[i]); + f(y[i]); +} diff --git a/js/src/jit-test/tests/warp/bug1681677.js b/js/src/jit-test/tests/warp/bug1681677.js new file mode 100644 index 0000000000..157a28740b --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1681677.js @@ -0,0 +1,7 @@ +var a = [1]; +var p = {__proto__: Array.prototype}; +Object.setPrototypeOf(a, p); +for (var i = 0; i < 100; ++i) { + var x = a.slice(0); + assertEq(x.__proto__, Array.prototype); +} diff --git a/js/src/jit-test/tests/warp/bug1681806.js b/js/src/jit-test/tests/warp/bug1681806.js new file mode 100644 index 0000000000..8dfdfd1b4c --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1681806.js @@ -0,0 +1,92 @@ +// |jit-test| skip-if: !getJitCompilerOptions()['ion.enable'] + +with ({}) {} + +let foo, bar, active; + +function test(depth) { + print(depth); + + // Define two mutually recursive functions with as many locals as possible + // to maximize the size of the rematerialized frame when we bail out (~4K). + foo = new Function('n', ` + var a0,b0,c0,d0,e0,f0,g0,h0,i0,j0,k0,l0,m0,n0,o0,p0,q0,r0,s0,t0,u0,v0,w0,x0,y0,z0; + var a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,o1,p1,q1,r1,s1,t1,u1,v1,w1,x1,y1,z1; + var a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,o2,p2,q2,r2,s2,t2,u2,v2,w2,x2,y2,z2; + var a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,o3,p3,q3,r3,s3,t3,u3,v3,w3,x3,y3,z3; + var a4,b4,c4,d4,e4,f4,g4,h4,i4,j4,k4,l4,m4,n4,o4,p4,q4,r4,s4,t4,u4,v4,w4,x4,y4,z4; + var a5,b5,c5,d5,e5,f5,g5,h5,i5,j5,k5,l5,m5,n5,o5,p5,q5,r5,s5,t5,u5,v5,w5,x5,y5,z5; + var a6,b6,c6,d6,e6,f6,g6,h6,i6,j6,k6,l6,m6,n6,o6,p6,q6,r6,s6,t6,u6,v6,w6,x6,y6,z6; + var a7,b7,c7,d7,e7,f7,g7,h7,i7,j7,k7,l7,m7,n7,o7,p7,q7,r7,s7,t7,u7,v7,w7,x7,y7,z7; + var a8,b8,c8,d8,e8,f8,g8,h8,i8,j8,k8,l8,m8,n8,o8,p8,q8,r8,s8,t8,u8,v8,w8,x8,y8,z8; + var a9,b9,c9,d9,e9,f9,g9,h9,i9,j9,k9,l9,m9,n9,o9,p9,q9,r9,s9,t9; + if (n == 0) { + if (active) invalidate(); + } else { + bar(n); + }`); + bar = new Function('n', ` + var a0,b0,c0,d0,e0,f0,g0,h0,i0,j0,k0,l0,m0,n0,o0,p0,q0,r0,s0,t0,u0,v0,w0,x0,y0,z0; + var a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,o1,p1,q1,r1,s1,t1,u1,v1,w1,x1,y1,z1; + var a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,o2,p2,q2,r2,s2,t2,u2,v2,w2,x2,y2,z2; + var a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,o3,p3,q3,r3,s3,t3,u3,v3,w3,x3,y3,z3; + var a4,b4,c4,d4,e4,f4,g4,h4,i4,j4,k4,l4,m4,n4,o4,p4,q4,r4,s4,t4,u4,v4,w4,x4,y4,z4; + var a5,b5,c5,d5,e5,f5,g5,h5,i5,j5,k5,l5,m5,n5,o5,p5,q5,r5,s5,t5,u5,v5,w5,x5,y5,z5; + var a6,b6,c6,d6,e6,f6,g6,h6,i6,j6,k6,l6,m6,n6,o6,p6,q6,r6,s6,t6,u6,v6,w6,x6,y6,z6; + var a7,b7,c7,d7,e7,f7,g7,h7,i7,j7,k7,l7,m7,n7,o7,p7,q7,r7,s7,t7,u7,v7,w7,x7,y7,z7; + var a8,b8,c8,d8,e8,f8,g8,h8,i8,j8,k8,l8,m8,n8,o8,p8,q8,r8,s8,t8,u8,v8,w8,x8,y8,z8; + var a9,b9,c9,d9,e9,f9,g9,h9,i9,j9,k9,l9,m9,n9,o9,p9,q9,r9,s9,t9; + foo(n-1);`); + + with ({}) {} + + // Warm up the invalidate() branch of foo to avoid FirstExecution bailouts. + active = true; + for (var i = 0; i < 10; i++) { + foo(2); + } + + // Warp-compile foo, inlining bar. + active = false; + for (var i = 0; i < 30; i++) { + foo(2); + } + + // Consume stack with frames that don't have to be invalidated. + function recurse(n) { + with ({}) {} + if (n == 0) { + foo(2); + } else { + recurse(n-1); + } + } + + // Trigger an invalidation. + active = true; + recurse(depth); +} + +// Binary search to find the right recursion depth such that +// the invalidation bailout will cause stack overflow. +let depth = 0; +function probeStackLimit(increment) { + try { + while (true) { + test(depth + increment); + depth += increment; + } + } catch {} +} + +probeStackLimit(8192); +probeStackLimit(4096); +probeStackLimit(2048); +probeStackLimit(1024); +probeStackLimit(512); +probeStackLimit(256); +probeStackLimit(128); +probeStackLimit(64); +probeStackLimit(32); +probeStackLimit(16); +probeStackLimit(8); diff --git a/js/src/jit-test/tests/warp/bug1683306.js b/js/src/jit-test/tests/warp/bug1683306.js new file mode 100644 index 0000000000..8d07524274 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1683306.js @@ -0,0 +1,19 @@ +// |jit-test| --ion-offthread-compile=off; --ion-full-warmup-threshold=0; --ion-gvn=off; --warp-async; --baseline-eager +// +// Bug 1683306 - Assertion failure: !genObj->hasStackStorage() || genObj->isStackStorageEmpty(), at vm/GeneratorObject.cpp:144 + +function assert(mustBeTrue, message) { } +assert.sameValue = function (expected) { + assert._toString(expected) +}; +assert._toString = function (value) { + return String(value); +} +async function fn() { + for await ([] of []) { } +} + +fn(); +bailAfter(10); +assert.sameValue(); +evaluate("fn();"); diff --git a/js/src/jit-test/tests/warp/bug1683309.js b/js/src/jit-test/tests/warp/bug1683309.js new file mode 100644 index 0000000000..097bed2622 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1683309.js @@ -0,0 +1,20 @@ +// |jit-test| slow; --ion-offthread-compile=off; --warp-async +// +// Bug 1683309: Assertion failure: [barrier verifier] Unmarked edge: JS Object 0xebbb6d1dee0 'object slot' edge to JS Object 0xebbb6d29f60, at gc/Verifier.cpp:392 +// +// The following testcase crashes on mozilla-central revision 20201217-2ab4142f19bc (debug build, run with --ion-offthread-compile=off --warp-async): + +if (helperThreadCount() > 0) { + evalInWorker(` + try{ + gczeal(4); + function f86(depth) { + var x = async target => ([]); + o62 = unescape; + x(o62.prop, o62); + f86(true + 1); + } + f86(0); + } catch (e) {} + `); +} diff --git a/js/src/jit-test/tests/warp/bug1683535-1.js b/js/src/jit-test/tests/warp/bug1683535-1.js new file mode 100644 index 0000000000..c85d301c3a --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1683535-1.js @@ -0,0 +1,6 @@ +function f(x, y) { + (Math.log() ? 0 : Math.abs(~y)) ^ x ? x : x; +} +for (let i = 0; i < 52; i++) { + f(0, -2147483649); +} diff --git a/js/src/jit-test/tests/warp/bug1683535-2.js b/js/src/jit-test/tests/warp/bug1683535-2.js new file mode 100644 index 0000000000..b1e26473c0 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1683535-2.js @@ -0,0 +1,10 @@ +function testMathyFunction(f, inputs) { + var results = []; + for (var j = 0; j < inputs.length; ++j) + for (var k = 0; k < inputs.length; ++k) + results.push(f(inputs[j], inputs[k])); +} +mathy0 = (function(x, y) { + return (Math.clz32((x <= x) >>> y) >> (~(0x080000000 >>> 0))) % Math.acos(~(2 ** 53)) >>> 0 +}); +testMathyFunction(mathy0, [1, 42, 0 / 0, 1 / 0, -Number.MIN_SAFE_INTEGER, -(2 ** 53), (2 ** 53), 1.7976931348623157e308]); diff --git a/js/src/jit-test/tests/warp/bug1683614.js b/js/src/jit-test/tests/warp/bug1683614.js new file mode 100644 index 0000000000..69fc780c15 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1683614.js @@ -0,0 +1,14 @@ +// |jit-test| --ion-offthread-compile=off; --ion-full-warmup-threshold=0; --warp-async; --baseline-eager; +// +// The following testcase crashes on mozilla-central revision 20201219-3262affdccf6 (debug build, run with --fuzzing-safe --ion-offthread-compile=off --ion-full-warmup-threshold=0 --warp-async --baseline-eager): +gczeal(9, 8); +function s() { } +new ReadableStream({ + start() { + test(); + } +}); +async function test() { + for (let i17 = 1; i17 <= 30; i17++) + await s(0 + function () { return i17 }); +}
\ No newline at end of file diff --git a/js/src/jit-test/tests/warp/bug1686207.js b/js/src/jit-test/tests/warp/bug1686207.js new file mode 100644 index 0000000000..4a2637408c --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1686207.js @@ -0,0 +1,11 @@ +function f(x, y) { + x >> (y >>> 0) +} + +with ({}) {} + +f(-1, -1) +f(1.5, 0) +for (var i = 0; i < 100; i++) { + f(0, 0); +} diff --git a/js/src/jit-test/tests/warp/bug1686702.js b/js/src/jit-test/tests/warp/bug1686702.js new file mode 100644 index 0000000000..b5bf4e4b54 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1686702.js @@ -0,0 +1,3 @@ +for (var j = 0; j < 100; j++) { + +(Math.fround(1) && 0); +} diff --git a/js/src/jit-test/tests/warp/bug1687661.js b/js/src/jit-test/tests/warp/bug1687661.js new file mode 100644 index 0000000000..e1b90a1879 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1687661.js @@ -0,0 +1,13 @@ +function f(x,y) { + return Math.trunc(+(y ? x : y) || ~y); +} + +with ({}) {} + +for (var i = 0; i < 10; i++) { + f(0,1); + f(NaN,1); + f(0.1,0); +} + +assertEq(f(0.1, 1), 0); diff --git a/js/src/jit-test/tests/warp/bug1687672.js b/js/src/jit-test/tests/warp/bug1687672.js new file mode 100644 index 0000000000..e5d1a746f3 --- /dev/null +++ b/js/src/jit-test/tests/warp/bug1687672.js @@ -0,0 +1,14 @@ +// |jit-test| --no-threads; --baseline-warmup-threshold=1; --ion-warmup-threshold=0 + +var input = ["", 0, "", "", "", "", "", "", "", "", "", "", "", "", {}, {}, ""]; + +for (var i = 0; i < 10; i++) { + function sum_indexing(x,i) { + if (i == x.length) { + return 0; + } else { + return x[i] + sum_indexing(x, i+1); + } + } + sum_indexing(input, 0); +} diff --git a/js/src/jit-test/tests/warp/cancel-offthread-compile.js b/js/src/jit-test/tests/warp/cancel-offthread-compile.js new file mode 100644 index 0000000000..939d128ae1 --- /dev/null +++ b/js/src/jit-test/tests/warp/cancel-offthread-compile.js @@ -0,0 +1,21 @@ +// |jit-test| --fast-warmup; skip-if: helperThreadCount() === 0 + +function foo(o) { + return o.y; +} + +with ({}) {} + +var sum = 0; + +// Trigger an off-thread Warp compile. +for (var i = 0; i < 30; i++) { + sum += foo({y: 1}); +} + +// Attach a new stub and cancel that compile. +for (var i = 0; i < 30; i++) { + sum += foo({x: 1, y: 1}); +} + +assertEq(sum, 60); diff --git a/js/src/jit-test/tests/warp/catch-overflow-regexp.js b/js/src/jit-test/tests/warp/catch-overflow-regexp.js new file mode 100644 index 0000000000..a316e5636b --- /dev/null +++ b/js/src/jit-test/tests/warp/catch-overflow-regexp.js @@ -0,0 +1,8 @@ +function test() { + try { + test(); + } catch { + /a/.test("a"); + } +} +test(); diff --git a/js/src/jit-test/tests/warp/force-warp.js b/js/src/jit-test/tests/warp/force-warp.js new file mode 100644 index 0000000000..b875fc0471 --- /dev/null +++ b/js/src/jit-test/tests/warp/force-warp.js @@ -0,0 +1,11 @@ +// A simple test to ensure WarpBuilder files are included in code-coverage builds. +// See bug 1635097. + +function test() { + var o = {x: 0}; + for (var i = 0; i < 10000; i++) { + o.x++; + } + return o; +} +test(); diff --git a/js/src/jit-test/tests/warp/function-load-length.js b/js/src/jit-test/tests/warp/function-load-length.js new file mode 100644 index 0000000000..c3ef3552eb --- /dev/null +++ b/js/src/jit-test/tests/warp/function-load-length.js @@ -0,0 +1,86 @@ +// Test transpiling of LoadFunctionLengthResult and cover possible bailout conditions. + +function empty() {} + +// Note: Typically won't use LoadFunctionLengthResult, because the "length" +// property will be resolved on the first access. +function testGlobalFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(empty.length, 0); + } +} +testGlobalFunction(); + +// Note: Typically won't use LoadFunctionLengthResult, because the "length" +// property will be resolved on the first access. +function testInnerFunction() { + function f() {} + for (var i = 0; i < 200; ++i) { + assertEq(f.length, 0); + } +} +testInnerFunction(); + +function testPerLoopFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(function(){}.length, 0); + } +} +testPerLoopFunction(); + +// Note: Typically won't use LoadFunctionLengthResult, because the "length" +// property will be resolved on the first access. +function testNativeFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(Math.max.length, 2); + } +} +testNativeFunction(); + +// Note: Typically won't use LoadFunctionLengthResult, because the "length" +// property will be resolved on the first access. +function testSelfHostedFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(Array.prototype.forEach.length, 1); + } +} +testSelfHostedFunction(); + +// Bailout when the length doesn't fit into int32. +function testBailoutLength() { + var values = [0, 0x80000000]; + var bound = empty.bind(); + + for (var i = 0; i < 10; ++i) { + var value = values[0 + (i >= 5)]; + + // Define on each iteration to get the same shape. + Object.defineProperty(bound, "length", {value}); + + for (var j = 0; j < 100; ++j) { + var f = bound.bind(); + assertEq(f.length, value); + } + } +} +testBailoutLength(); + +// Bailout when trying to read "length" from a property with a lazy script. +function testBailoutLazyFunction() { + for (var i = 0; i < 200; ++i) { + var values = [function(){}, function(a){}]; + var index = 0 + (i >= 100); + assertEq(values[index].length, index); + } +} +testBailoutLazyFunction(); + +// Bailout when trying to read "length" from a property with a lazy self-hosted script. +function testBailoutLazySelfHostedFunction() { + for (var i = 0; i < 200; ++i) { + var values = [function(){}, Array.prototype.map]; + var index = 0 + (i >= 100); + assertEq(values[index].length, index); + } +} +testBailoutLazySelfHostedFunction(); diff --git a/js/src/jit-test/tests/warp/function-load-name.js b/js/src/jit-test/tests/warp/function-load-name.js new file mode 100644 index 0000000000..686ed32629 --- /dev/null +++ b/js/src/jit-test/tests/warp/function-load-name.js @@ -0,0 +1,100 @@ +// Test transpiling of LoadFunctionNameResult and cover possible bailout conditions. + +function empty() {} + +// Note: Typically won't use LoadFunctionNameResult, because the "name" +// property will be resolved on the first access. +function testGlobalFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(empty.name, "empty"); + } +} +testGlobalFunction(); + +// Note: Typically won't use LoadFunctionNameResult, because the "name" +// property will be resolved on the first access. +function testInnerFunction() { + function f() {} + for (var i = 0; i < 200; ++i) { + assertEq(f.name, "f"); + } +} +testInnerFunction(); + +function testPerLoopFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(function f(){}.name, "f"); + } +} +testPerLoopFunction(); + +// Note: Typically won't use LoadFunctionNameResult, because the "name" +// property will be resolved on the first access. +function testNativeFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(Math.max.name, "max"); + } +} +testNativeFunction(); + +// Note: Typically won't use LoadFunctionNameResult, because the "name" +// property will be resolved on the first access. +function testSelfHostedFunction() { + for (var i = 0; i < 200; ++i) { + assertEq(Array.prototype.forEach.name, "forEach"); + } +} +testSelfHostedFunction(); + +// Bailout when the name property is resolved. +function testBailoutResolvedName() { + function f1() {} + + // Ensure the name property of |f1| is resolved. + assertEq(f1.name, "f1"); + + var names = ["f", "f1"]; + + for (var i = 0; i < 10; ++i) { + var name = names[0 + (i >= 5)]; + + for (var j = 0; j < 100; ++j) { + var values = [function f(){}, f1]; + var value = values[0 + (i >= 5)]; + + assertEq(value.name, name); + } + } +} +testBailoutResolvedName(); + +// Bailout when the HAS_BOUND_FUNCTION_NAME_PREFIX isn't set. +function testBailoutBoundName() { + function f1() {} + function f2() {} + + var bound = f1.bind(); + + // Ensure the name property of |bound| is resolved. That way new functions + // created through |bound().bind()| will have the HAS_BOUND_FUNCTION_NAME_PREFIX + // flag set. + assertEq(bound.name, "bound f1"); + + // |bound1| and |bound2| have the same shape, but different function flags. + var bound1 = bound.bind(); // HAS_BOUND_FUNCTION_NAME_PREFIX + var bound2 = f2.bind(); // ! HAS_BOUND_FUNCTION_NAME_PREFIX + + var values = [bound1, bound2]; + var names = ["bound bound bound f1", "bound bound f2"]; + + for (var i = 0; i < 10; ++i) { + var value = values[0 + (i >= 5)]; + var name = names[0 + (i >= 5)]; + + for (var j = 0; j < 100; ++j) { + var f = value.bind(); + assertEq(f.name, name); + } + } +} +testBailoutBoundName(); diff --git a/js/src/jit-test/tests/warp/guard-function-is-non-builtin-ctor.js b/js/src/jit-test/tests/warp/guard-function-is-non-builtin-ctor.js new file mode 100644 index 0000000000..c2b92d4510 --- /dev/null +++ b/js/src/jit-test/tests/warp/guard-function-is-non-builtin-ctor.js @@ -0,0 +1,20 @@ +function test() { + for (var i = 0; i <= 200; ++i) { + // Create a fresh function in each iteration. + var values = [function(){}, () => {}]; + + // Use an arrow function in the last iteration. + var useArrowFn = (i === 200); + + // No conditional (?:) so we don't trigger a cold-code bailout. + var value = values[0 + useArrowFn]; + + // Set or create the "prototype" property. + value.prototype = null; + + // The "prototype" is configurable iff the function is an arrow function. + var desc = Object.getOwnPropertyDescriptor(value, "prototype"); + assertEq(desc.configurable, useArrowFn); + } +} +test(); diff --git a/js/src/jit-test/tests/warp/guard-has-getter-setter.js b/js/src/jit-test/tests/warp/guard-has-getter-setter.js new file mode 100644 index 0000000000..dc4b1d9ebd --- /dev/null +++ b/js/src/jit-test/tests/warp/guard-has-getter-setter.js @@ -0,0 +1,263 @@ +// Access property once. +function simple() { + var obj = { + get p() { + return 1; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + r += o.p; + } + assertEq(r, 200); +} +simple(); + +// Access property multiple times (consecutive) to test that MGuardHasGetterSetter +// ops can be merged. +function consecutive() { + var obj = { + get p() { + return 1; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + + r += o.p; + r += o.p; + r += o.p; + r += o.p; + } + assertEq(r, 4 * 200); +} +consecutive(); + +// Access property multiple times (loop) to test LICM. +function loop() { + var obj = { + get p() { + return 1; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + + for (var j = 0; j < 5; ++j) { + r += o.p; + } + } + assertEq(r, 5 * 200); +} +loop(); + +// Bailout when prototype changes. +function modifyProto() { + var obj = { + get p() { + return 1; + } + }; + + var obj2 = { + get p() { + return 2; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + + r += o.p; + + // Always execute Object.setPrototypeOf() to avoid cold code bailouts, + // which would happen for conditional code like if-statements. But only + // actually change |o|'s prototype once. + var j = (i === 100) | 0; + var q = [{}, o][j]; + Object.setPrototypeOf(q, obj2); + + r += o.p; + } + assertEq(r, 2 * 200 + Math.floor(100 / 8) * 2 + 1); +} +modifyProto(); + +// Bailout when property is changed to own data property. +function modifyToOwnValue() { + var obj = { + get p() { + return 1; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + + r += o.p; + + // Always execute Object.setPrototypeOf() to avoid cold code bailouts, + // which would happen for conditional code like if-statements. But only + // actually change |o|'s prototype once. + var j = (i === 100) | 0; + var q = [{}, o][j]; + Object.defineProperty(q, "p", {value: 2}); + + r += o.p; + } + assertEq(r, 2 * 200 + Math.floor(100 / 8) * 2 + 1); +} +modifyToOwnValue(); + +// Bailout when property is changed to own accessor property. +function modifyToOwnAccessor() { + var obj = { + get p() { + return 1; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + + r += o.p; + + // Always execute Object.setPrototypeOf() to avoid cold code bailouts, + // which would happen for conditional code like if-statements. But only + // actually change |o|'s prototype once. + var j = (i === 100) | 0; + var q = [{}, o][j]; + Object.defineProperty(q, "p", {get() { return 2; }}); + + r += o.p; + } + assertEq(r, 2 * 200 + Math.floor(100 / 8) * 2 + 1); +} +modifyToOwnAccessor(); + +// Bailout when changing accessor. +function modifyProtoAccessor() { + var obj = { + get p() { + return 1; + } + }; + + // Use objects with different shapes to enter megamorphic state for + // the JSOp::GetProp opcode. + var array = [ + Object.create(obj, {a: {value: 1}}), + Object.create(obj, {b: {value: 2}}), + Object.create(obj, {c: {value: 3}}), + Object.create(obj, {d: {value: 4}}), + Object.create(obj, {e: {value: 5}}), + Object.create(obj, {f: {value: 6}}), + Object.create(obj, {g: {value: 7}}), + Object.create(obj, {h: {value: 8}}), + ]; + + var r = 0; + for (var i = 0; i < 200; ++i) { + var o = array[i & 7]; + + r += o.p; + + // Always execute Object.setPrototypeOf() to avoid cold code bailouts, + // which would happen for conditional code like if-statements. But only + // actually change |o|'s prototype once. + var j = (i === 100) | 0; + var q = [{}, obj][j]; + Object.defineProperty(q, "p", {get() { return 2; }}); + + r += o.p; + } + assertEq(r, 2 * 200 + 100 * 2 - 1); +} +modifyProtoAccessor(); diff --git a/js/src/jit-test/tests/warp/guard-string-to-number-or-int32.js b/js/src/jit-test/tests/warp/guard-string-to-number-or-int32.js new file mode 100644 index 0000000000..8a28d46f99 --- /dev/null +++ b/js/src/jit-test/tests/warp/guard-string-to-number-or-int32.js @@ -0,0 +1,36 @@ +function stringToNumber() { + function f(s) { + return ~~s; + } + + var q = 0; + for (var i = 0; i < 200; ++i) { + q += f("1"); + q += f("0x2"); + q += f("0b11"); + q += f("0o4"); + + // Invalid inputs: ~~Nan == 0 + q += f("z"); + q += f("0x2.3"); + q += f("0x1.fp4"); + } + assertEq(q, (1 + 2 + 3 + 4) * 200); +} +stringToNumber(); + +function stringToInt32() { + function f(s) { + return s - 0; + } + + var q = 0; + for (var i = 0; i < 200; ++i) { + q += f("1"); + q += f("0x2"); + q += f("0b11"); + q += f("0o4"); + } + assertEq(q, (1 + 2 + 3 + 4) * 200); +} +stringToInt32(); diff --git a/js/src/jit-test/tests/warp/guardproto-nursery.js b/js/src/jit-test/tests/warp/guardproto-nursery.js new file mode 100644 index 0000000000..5093298e88 --- /dev/null +++ b/js/src/jit-test/tests/warp/guardproto-nursery.js @@ -0,0 +1,13 @@ +function f() { + var o = {x: 1, y: 3}; + o.__proto__ = {x: 2}; + var p = Math; + p.__proto__ = o; + p.__proto__ = {__proto__: o}; + + for (var i = 0; i < 3000; i++) { + assertEq(p.x, 1); + assertEq(p.y, 3); + } +} +f(); diff --git a/js/src/jit-test/tests/warp/inline-array-at.js b/js/src/jit-test/tests/warp/inline-array-at.js new file mode 100644 index 0000000000..691695151c --- /dev/null +++ b/js/src/jit-test/tests/warp/inline-array-at.js @@ -0,0 +1,17 @@ +// |jit-test| skip-if: !Array.prototype.at + +function f(x) { + assertEq(x.at(0), 1); + assertEq(x.at(-1), 3); + assertEq(x.at(10), undefined); +} + +function g() { + for (var i = 0; i < 100; i++) { + f([1, 2, 3]); + } +} + +for (var j = 0; j < 10; j++) { + g(); +} diff --git a/js/src/jit-test/tests/warp/load-unboxed-typedarray-bigint.js b/js/src/jit-test/tests/warp/load-unboxed-typedarray-bigint.js new file mode 100644 index 0000000000..736e80f3f5 --- /dev/null +++ b/js/src/jit-test/tests/warp/load-unboxed-typedarray-bigint.js @@ -0,0 +1,7 @@ +var ta = new BigInt64Array([0n, 1n]); +var q = 0; +for (var i = 0; i < 10000; ++i) { + if (ta[i&1]) q++; +} + +assertEq(q, 5000); diff --git a/js/src/jit-test/tests/warp/math-indirect-truncate.js b/js/src/jit-test/tests/warp/math-indirect-truncate.js new file mode 100644 index 0000000000..9cc967ca33 --- /dev/null +++ b/js/src/jit-test/tests/warp/math-indirect-truncate.js @@ -0,0 +1,55 @@ +function testCeil() { + function ceil(a, b) { + return Math.ceil(a / b) | 0; + } + + // Warm-up + for (var i = 0; i < 50; i++) { + ceil(5, 5); + } + + assertEq(ceil(5, 3), 2); +} +testCeil(); + +function testFloor() { + function floor(a, b) { + return Math.floor(a / b) | 0; + } + + // Warm-up + for (var i = 0; i < 50; i++) { + floor(5, 5); + } + + assertEq(floor(-5, 3), -2); +} +testFloor(); + +function testRound() { + function round(a, b) { + return Math.round(a / b) | 0; + } + + // Warm-up + for (var i = 0; i < 50; i++) { + round(5, 5); + } + + assertEq(round(5, 3), 2); +} +testRound(); + +function testTrunc() { + function trunc(a, b) { + return Math.trunc(a / b) | 0; + } + + // Warm-up + for (var i = 0; i < 50; i++) { + trunc(5, 5); + } + + assertEq(trunc(5, 3), 1); +} +testTrunc(); diff --git a/js/src/jit-test/tests/warp/mega-morphic-load-and-has-prop.js b/js/src/jit-test/tests/warp/mega-morphic-load-and-has-prop.js new file mode 100644 index 0000000000..0d77ceb316 --- /dev/null +++ b/js/src/jit-test/tests/warp/mega-morphic-load-and-has-prop.js @@ -0,0 +1,99 @@ +function testMegamorphicLoadSlot(i) { + var xs = [ + {p: 0}, + {a: 0, p: 1}, + {a: 0, b: 0, p: 2}, + {a: 0, b: 0, c: 0, p: 3}, + {a: 0, b: 0, c: 0, d: 0, p: 4}, + {a: 0, b: 0, c: 0, d: 0, e: 0, p: 5}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, p: 6}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, p: 7}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, p: 8}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, i: 0, p: 9}, + ]; + var called = 0; + var obj = { + get p() { + called++; + } + }; + + for (var j = 0; j <= 100; ++j) { + // Don't use if-statements to avoid cold code bailouts. + var x = xs[j % 10]; + var y = [x, obj][(i === 1 && j === 100)|0]; + + // Can't DCE this instruction. + y.p; + } + + assertEq(i === 0 || called === 1, true); +} +for (var i = 0; i < 2; ++i) testMegamorphicLoadSlot(i); + +function testMegamorphicLoadSlotByValue(i) { + var xs = [ + {p: 0}, + {a: 0, p: 1}, + {a: 0, b: 0, p: 2}, + {a: 0, b: 0, c: 0, p: 3}, + {a: 0, b: 0, c: 0, d: 0, p: 4}, + {a: 0, b: 0, c: 0, d: 0, e: 0, p: 5}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, p: 6}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, p: 7}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, p: 8}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, i: 0, p: 9}, + ]; + var called = 0; + var obj = { + get p() { + called++; + } + }; + + var p = "p"; + for (var j = 0; j <= 100; ++j) { + // Don't use if-statements to avoid cold code bailouts. + var x = xs[j % 10]; + var y = [x, obj][(i === 1 && j === 100)|0]; + + // Can't DCE this instruction. + y[p]; + } + + assertEq(i === 0 || called === 1, true); +} +for (var i = 0; i < 2; ++i) testMegamorphicLoadSlotByValue(i); + +function testMegamorphicHasProp(i) { + var xs = [ + {p: 0}, + {a: 0, p: 1}, + {a: 0, b: 0, p: 2}, + {a: 0, b: 0, c: 0, p: 3}, + {a: 0, b: 0, c: 0, d: 0, p: 4}, + {a: 0, b: 0, c: 0, d: 0, e: 0, p: 5}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, p: 6}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, p: 7}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, p: 8}, + {a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, i: 0, p: 9}, + ]; + var called = 0; + var obj = new Proxy({}, { + has() { + called++; + } + }); + + for (var j = 0; j <= 100; ++j) { + // Don't use if-statements to avoid cold code bailouts. + var x = xs[j % 10]; + var y = [x, obj][(i === 1 && j === 100)|0]; + + // Can't DCE this instruction. + "p" in y; + } + + assertEq(i === 0 || called === 1, true); +} +for (var i = 0; i < 2; ++i) testMegamorphicHasProp(i); diff --git a/js/src/jit-test/tests/warp/non-int32-array-length.js b/js/src/jit-test/tests/warp/non-int32-array-length.js new file mode 100644 index 0000000000..4213f1991c --- /dev/null +++ b/js/src/jit-test/tests/warp/non-int32-array-length.js @@ -0,0 +1,10 @@ +function f(arr, len) { + for (var i = 0; i < 2000; i++) { + assertEq(arr.length, len); + } +} +var arr = [0]; +f(arr, 1); + +arr.length = 0xffff_ffff; +f(arr, 0xffff_ffff); diff --git a/js/src/jit-test/tests/warp/null-not-zero-index.js b/js/src/jit-test/tests/warp/null-not-zero-index.js new file mode 100644 index 0000000000..b55b743b95 --- /dev/null +++ b/js/src/jit-test/tests/warp/null-not-zero-index.js @@ -0,0 +1,17 @@ +// |jit-test| --no-threads + +function f(index) { + var a = [123]; + return a[index] +} + +function g() { + for (var i = 0; i < 100; i++) { + // Make sure |null| is not treated like a |0| index. + assertEq(f(i > 90 ? null : 0), i > 90 ? undefined : 123) + } +} + +for (var j = 0; j < 10; j++) { + g(); +} diff --git a/js/src/jit-test/tests/warp/object-class-tostring.js b/js/src/jit-test/tests/warp/object-class-tostring.js new file mode 100644 index 0000000000..d26a2d95a5 --- /dev/null +++ b/js/src/jit-test/tests/warp/object-class-tostring.js @@ -0,0 +1,65 @@ +function testCongruent(i) { + var p = {}; + var o = { + // Add toString as an own property, so it'll be always found on this object, + // even when properties are changed on the prototype. + toString: Object.prototype.toString, + + // Add a custom prototype, so we can add @@toStringTag without modifying the + // shape of this object. + __proto__: p, + }; + var xs = [{}, p]; + var ys = ["[object Object]", "[object Test]"]; + + for (var j = 0; j <= 100; ++j) { + // Don't use if-statements to avoid cold code bailouts + var x = xs[(i === 1 && j === 100)|0]; + var y = ys[(i === 1 && j === 100)|0]; + + // |o.toString()| must be executed twice, because |x[Symbol.toStringTag]| may + // have modified |o|. + var r = o.toString(); + x[Symbol.toStringTag] = "Test"; + var e = o.toString(); + + assertEq(r, "[object Object]"); + assertEq(e, y); + } +} +for (var i = 0; i < 2; ++i) testCongruent(i); + +function testUnobserved(i) { + var p = {}; + var o = { + // Add toString as an own property, so it'll be always found on this object, + // even when properties are changed on the prototype. + toString: Object.prototype.toString, + + // Add a custom prototype, so we can add @@toStringTag without modifying the + // shape of this object. + __proto__: p, + }; + var xs = [{}, p]; + var ys = [false, true]; + + for (var j = 0; j <= 100; ++j) { + // Don't use if-statements to avoid cold code bailouts + var x = xs[(i === 1 && j === 100)|0]; + var y = ys[(i === 1 && j === 100)|0]; + + var executed = false; + Object.defineProperty(x, Symbol.toStringTag, { + configurable: true, + get() { + executed = true; + } + }); + + // |o.toString()| must be executed even when the result isn't observed. + o.toString(); + + assertEq(executed, y); + } +} +for (var i = 0; i < 2; ++i) testUnobserved(i); diff --git a/js/src/jit-test/tests/warp/phi-specialization.js b/js/src/jit-test/tests/warp/phi-specialization.js new file mode 100644 index 0000000000..a466a9eb9b --- /dev/null +++ b/js/src/jit-test/tests/warp/phi-specialization.js @@ -0,0 +1,42 @@ +// |jit-test| --fast-warmup + +var sum = 0; + +function foo(copy, shouldThrow) { + switch (copy) { + case 0: + var x = 0; + try { + if (shouldThrow) { throw 0;} + x = 1; + } catch { + x = "a"; + } + // We create a specialized phi for x here, which bails out. + for (var i = 0; i < 100; i++) { + sum += x; + } + break; + case 1: + var y = 0; + try { + if (shouldThrow) { throw 0;} + y = 1; + } catch { + y = "a"; + } + // We do not create a specialized phi the second time. + for (var i = 0; i < 100; i++) { + sum += y; + } + break; + } +} + +with ({}) {} +for (var i = 0; i < 2; i++) { + for (var j = 0; j < 50; j++) { + foo(i, false); + } + foo(i, true); +} diff --git a/js/src/jit-test/tests/warp/property-add-shape.js b/js/src/jit-test/tests/warp/property-add-shape.js new file mode 100644 index 0000000000..88348200bf --- /dev/null +++ b/js/src/jit-test/tests/warp/property-add-shape.js @@ -0,0 +1,98 @@ +function simple() { + var o = {a: 1}; + o.b = 2; + + assertEq(o.a, 1); + assertEq(o.b, 2); +} + +function condition1(b) { + var o = {a: 1}; + + if (b) { + o.b = 2; + } + + o.c = 3; + + assertEq(o.a, 1); + if (b) { + assertEq(o.b, 2); + } else { + assertEq('b' in o, false); + } + assertEq(o.c, 3); +} + +function condition2(b) { + var o = {a: 1}; + + if (b) { + o.b = 2; + } else { + o.b = 3; + } + + o.c = 3; + + assertEq(o.a, 1); + assertEq(o.b, b ? 2 : 3); + assertEq(o.c, 3); +} + +function condition3(b) { + var o = {a: 1}; + + if (b) { + o.b = 2; + } else { + o.b = 2; + } + + o.c = 3; + + assertEq(o.a, 1); + assertEq(o.b, 2); + assertEq(o.c, 3); +} + +function condition4(b) { + var o = {a: 1}; + + o.bla = 2; + o.bla2 = 2; + o.bla3 = 2; + o.bla4 = 2; + + if (b) { + o.b = 2; + } else { + o.c = 2; + } + + o.d = 3; + + assertEq(o.a, 1); + if (b) { + assertEq(o.b, 2); + assertEq('c' in o, false); + } else { + assertEq('b' in o, false); + assertEq(o.c, 2); + } + assertEq(o.d, 3); +} + +function f() { + for (var i = 0; i < 10; i++) { + simple(); + condition1(i % 2 == 0) + condition2(i % 2 == 0) + condition3(i % 2 == 0) + condition4(i % 2 == 0) + } +} + +for (var i = 0; i < 10; i++) { + f(); +} diff --git a/js/src/jit-test/tests/warp/small-inlinable-builtins.js b/js/src/jit-test/tests/warp/small-inlinable-builtins.js new file mode 100644 index 0000000000..d346894f6a --- /dev/null +++ b/js/src/jit-test/tests/warp/small-inlinable-builtins.js @@ -0,0 +1,10 @@ +// Ensure certain self-hosted built-in functions are small enough to be inlinable. + +assertEq(isSmallFunction(isFinite), true); +assertEq(isSmallFunction(isNaN), true); + +assertEq(isSmallFunction(Number.isFinite), true); +assertEq(isSmallFunction(Number.isNaN), true); + +assertEq(isSmallFunction(Number.isInteger), true); +assertEq(isSmallFunction(Number.isSafeInteger), true); diff --git a/js/src/jit-test/tests/warp/string-char.js b/js/src/jit-test/tests/warp/string-char.js new file mode 100644 index 0000000000..fc41e5079a --- /dev/null +++ b/js/src/jit-test/tests/warp/string-char.js @@ -0,0 +1,15 @@ +function f(x) { + assertEq(x.charCodeAt(1), 0x62); + assertEq(x.charAt(1), "b"); + assertEq(x[1], "b"); +} + +function g() { + for (var i = 0; i < 100; i++) { + f("abc"); + } +} + +for (var j = 0; j < 10; j++) { + g(); +} diff --git a/js/src/jit-test/tests/warp/super-native-newtarget.js b/js/src/jit-test/tests/warp/super-native-newtarget.js new file mode 100644 index 0000000000..3f9fb24a3a --- /dev/null +++ b/js/src/jit-test/tests/warp/super-native-newtarget.js @@ -0,0 +1,20 @@ +class A {} + +class B extends A { + constructor() { + super(); + } +} + +function h() {} +h = h.bind(); + +function f() { + for (var i = 0; i < 1000; ++i) { + var o = Reflect.construct(B, [], h); + } +} + +for (var i = 0; i < 5; ++i) { + f(); +} diff --git a/js/src/jit-test/tests/warp/typedarray-element-exists.js b/js/src/jit-test/tests/warp/typedarray-element-exists.js new file mode 100644 index 0000000000..623ad672cb --- /dev/null +++ b/js/src/jit-test/tests/warp/typedarray-element-exists.js @@ -0,0 +1,46 @@ +function inBounds() { + var ta = new Int32Array(10); + + for (var i = 0; i < 100; ++i) { + var index = i & 7; + assertEq(index in ta, true); + } +} +inBounds(); + +function outOfBounds() { + var ta = new Int32Array(10); + + for (var i = 0; i < 100; ++i) { + var index = 10 + (i & 7); + assertEq(index in ta, false); + + var largeIndex = 2147483647 - (i & 1); + assertEq(largeIndex in ta, false); + } +} +outOfBounds(); + +function negativeIndex() { + var ta = new Int32Array(10); + + for (var i = 0; i < 100; ++i) { + var index = -(1 + (i & 7)); + assertEq(index in ta, false); + + var largeIndex = -2147483647 - (i & 1); + assertEq(largeIndex in ta, false); + } +} +negativeIndex(); + +function emptyArray() { + var ta = new Int32Array(0); + + for (var i = 0; i < 100; ++i) { + var index = i & 7; + assertEq(index in ta, false); + assertEq(-index in ta, false); + } +} +emptyArray(); diff --git a/js/src/jit-test/tests/warp/typedarrayindextoint32.js b/js/src/jit-test/tests/warp/typedarrayindextoint32.js new file mode 100644 index 0000000000..5333ada53e --- /dev/null +++ b/js/src/jit-test/tests/warp/typedarrayindextoint32.js @@ -0,0 +1,10 @@ +function f(ta, i) { + var x = i + 0.2; + return ta[i] + ta[i | 0] + ta[x - 0.2]; +} + +var ta = new Int32Array(10); +var xs = [0, 1, 2, -1]; +for (var i = 0; i < 100_000; ++i) { + assertEq(f(ta, xs[i & 3]), (i & 3) == 3 ? NaN : 0); +} |