summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/warp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/warp')
-rw-r--r--js/src/jit-test/tests/warp/arguments-object-load-arg.js109
-rw-r--r--js/src/jit-test/tests/warp/arguments-object-load-length.js57
-rw-r--r--js/src/jit-test/tests/warp/bailout-inline-fun-call-no-args.js19
-rw-r--r--js/src/jit-test/tests/warp/bailout-inline-getter.js22
-rw-r--r--js/src/jit-test/tests/warp/booleantostring.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1646041.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1646302.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1647054.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1652049.js7
-rw-r--r--js/src/jit-test/tests/warp/bug1652732.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1653913.js3
-rw-r--r--js/src/jit-test/tests/warp/bug1653972.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1661530.js7
-rw-r--r--js/src/jit-test/tests/warp/bug1661728.js43
-rw-r--r--js/src/jit-test/tests/warp/bug1662146.js12
-rw-r--r--js/src/jit-test/tests/warp/bug1663993.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1664007.js12
-rw-r--r--js/src/jit-test/tests/warp/bug1665303.js20
-rw-r--r--js/src/jit-test/tests/warp/bug1666070.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1666142-1.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1666142-2.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1667680.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1667685.js27
-rw-r--r--js/src/jit-test/tests/warp/bug1667699.js15
-rw-r--r--js/src/jit-test/tests/warp/bug1668197.js6
-rw-r--r--js/src/jit-test/tests/warp/bug1669415.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1669597.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1671812.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1676631.js7
-rw-r--r--js/src/jit-test/tests/warp/bug1676639.js7
-rw-r--r--js/src/jit-test/tests/warp/bug1681056.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1681597.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1681677.js7
-rw-r--r--js/src/jit-test/tests/warp/bug1681806.js92
-rw-r--r--js/src/jit-test/tests/warp/bug1683306.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1683309.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1683535-1.js6
-rw-r--r--js/src/jit-test/tests/warp/bug1683535-2.js12
-rw-r--r--js/src/jit-test/tests/warp/bug1683614.js13
-rw-r--r--js/src/jit-test/tests/warp/bug1686207.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1686702.js3
-rw-r--r--js/src/jit-test/tests/warp/bug1687661.js13
-rw-r--r--js/src/jit-test/tests/warp/bug1687672.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1688136.js13
-rw-r--r--js/src/jit-test/tests/warp/bug1688346.js12
-rw-r--r--js/src/jit-test/tests/warp/bug1692857.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1693062-01.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1693062-02.js16
-rw-r--r--js/src/jit-test/tests/warp/bug1694600.js16
-rw-r--r--js/src/jit-test/tests/warp/bug1696897.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1697451.js15
-rw-r--r--js/src/jit-test/tests/warp/bug1697483.js46
-rw-r--r--js/src/jit-test/tests/warp/bug1698126.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1698609.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1699056.js12
-rw-r--r--js/src/jit-test/tests/warp/bug1700579.js28
-rw-r--r--js/src/jit-test/tests/warp/bug1700616.js32
-rw-r--r--js/src/jit-test/tests/warp/bug1701208.js18
-rw-r--r--js/src/jit-test/tests/warp/bug1702465.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1703766.js212
-rw-r--r--js/src/jit-test/tests/warp/bug1703817.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1704467.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1706314.js9
-rw-r--r--js/src/jit-test/tests/warp/bug1708839.js16
-rw-r--r--js/src/jit-test/tests/warp/bug1713579.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1716231.js25
-rw-r--r--js/src/jit-test/tests/warp/bug1716931.js13
-rw-r--r--js/src/jit-test/tests/warp/bug1719884.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1720093-1.js17
-rw-r--r--js/src/jit-test/tests/warp/bug1720093-2.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1732601.js27
-rw-r--r--js/src/jit-test/tests/warp/bug1735157.js5
-rw-r--r--js/src/jit-test/tests/warp/bug1738676.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1741635-1.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1741635-2.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1745949.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1761947.js28
-rw-r--r--js/src/jit-test/tests/warp/bug1762769.js8
-rw-r--r--js/src/jit-test/tests/warp/bug1762770.js13
-rw-r--r--js/src/jit-test/tests/warp/bug1763012-1.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1763012-2.js19
-rw-r--r--js/src/jit-test/tests/warp/bug1767196.js15
-rw-r--r--js/src/jit-test/tests/warp/bug1769410.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1770904.js7
-rw-r--r--js/src/jit-test/tests/warp/bug1789821.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1825220.js11
-rw-r--r--js/src/jit-test/tests/warp/bug1825408.js20
-rw-r--r--js/src/jit-test/tests/warp/bug1852238.js14
-rw-r--r--js/src/jit-test/tests/warp/bug1852398.js27
-rw-r--r--js/src/jit-test/tests/warp/bug1852702.js17
-rw-r--r--js/src/jit-test/tests/warp/cancel-offthread-compile.js21
-rw-r--r--js/src/jit-test/tests/warp/catch-overflow-regexp.js8
-rw-r--r--js/src/jit-test/tests/warp/compare-constant-string.js90
-rw-r--r--js/src/jit-test/tests/warp/compare-empty-string.js50
-rw-r--r--js/src/jit-test/tests/warp/conditional-test-guard.js12
-rw-r--r--js/src/jit-test/tests/warp/conditional-test-undefined-1.js22
-rw-r--r--js/src/jit-test/tests/warp/conditional-test-undefined-2.js22
-rw-r--r--js/src/jit-test/tests/warp/force-warp.js11
-rw-r--r--js/src/jit-test/tests/warp/fun-call-not-inlined.js29
-rw-r--r--js/src/jit-test/tests/warp/function-load-length.js86
-rw-r--r--js/src/jit-test/tests/warp/function-load-name.js100
-rw-r--r--js/src/jit-test/tests/warp/function-var-environment-inlined.js15
-rw-r--r--js/src/jit-test/tests/warp/function-var-environment.js43
-rw-r--r--js/src/jit-test/tests/warp/guard-function-is-non-builtin-ctor.js20
-rw-r--r--js/src/jit-test/tests/warp/guard-has-getter-setter.js263
-rw-r--r--js/src/jit-test/tests/warp/guard-specific-atom-with-short-atom.js82
-rw-r--r--js/src/jit-test/tests/warp/guard-string-to-number-or-int32.js36
-rw-r--r--js/src/jit-test/tests/warp/guardproto-nursery.js13
-rw-r--r--js/src/jit-test/tests/warp/inline-array-at.js15
-rw-r--r--js/src/jit-test/tests/warp/inlined-accessor-exc-bailout.js45
-rw-r--r--js/src/jit-test/tests/warp/load-unboxed-typedarray-bigint.js7
-rw-r--r--js/src/jit-test/tests/warp/map-get-bigint.js211
-rw-r--r--js/src/jit-test/tests/warp/map-get-nongcthing.js343
-rw-r--r--js/src/jit-test/tests/warp/map-get-object.js104
-rw-r--r--js/src/jit-test/tests/warp/map-get-string.js162
-rw-r--r--js/src/jit-test/tests/warp/map-get-symbol.js82
-rw-r--r--js/src/jit-test/tests/warp/map-get-value.js104
-rw-r--r--js/src/jit-test/tests/warp/map-has-bigint.js192
-rw-r--r--js/src/jit-test/tests/warp/map-has-nongcthing.js307
-rw-r--r--js/src/jit-test/tests/warp/map-has-object.js97
-rw-r--r--js/src/jit-test/tests/warp/map-has-string.js147
-rw-r--r--js/src/jit-test/tests/warp/map-has-symbol.js75
-rw-r--r--js/src/jit-test/tests/warp/map-has-value.js97
-rw-r--r--js/src/jit-test/tests/warp/math-indirect-truncate.js55
-rw-r--r--js/src/jit-test/tests/warp/mega-morphic-load-and-has-prop.js99
-rw-r--r--js/src/jit-test/tests/warp/min-max-foldsTo-1.js47
-rw-r--r--js/src/jit-test/tests/warp/min-max-foldsTo-2.js153
-rw-r--r--js/src/jit-test/tests/warp/min-max-foldsTo-3.js177
-rw-r--r--js/src/jit-test/tests/warp/min-max-foldsTo-4.js11
-rw-r--r--js/src/jit-test/tests/warp/non-int32-array-length.js10
-rw-r--r--js/src/jit-test/tests/warp/null-not-zero-index.js17
-rw-r--r--js/src/jit-test/tests/warp/number-tostring-with-base-uppercase.js92
-rw-r--r--js/src/jit-test/tests/warp/number-tostring-with-base.js89
-rw-r--r--js/src/jit-test/tests/warp/object-class-tostring.js65
-rw-r--r--js/src/jit-test/tests/warp/phi-specialization.js42
-rw-r--r--js/src/jit-test/tests/warp/polymorphic-to-bool.js39
-rw-r--r--js/src/jit-test/tests/warp/property-add-shape.js98
-rw-r--r--js/src/jit-test/tests/warp/rest-elements.js40
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-apply-array-01.js21
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-apply-array-02.js19
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-apply-array-03.js23
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-apply-array-04.js24
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-apply-array.js43
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-construct-array-01.js28
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-construct-array-02.js26
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-construct-array-03.js32
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-construct-array-04.js26
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-construct-array.js48
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-array-iterator-next.js24
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-rest-apply-array.js38
-rw-r--r--js/src/jit-test/tests/warp/scalar-replace-rest-construct-array.js43
-rw-r--r--js/src/jit-test/tests/warp/set-has-bigint.js192
-rw-r--r--js/src/jit-test/tests/warp/set-has-nongcthing.js307
-rw-r--r--js/src/jit-test/tests/warp/set-has-object.js97
-rw-r--r--js/src/jit-test/tests/warp/set-has-string.js147
-rw-r--r--js/src/jit-test/tests/warp/set-has-symbol.js75
-rw-r--r--js/src/jit-test/tests/warp/set-has-value.js97
-rw-r--r--js/src/jit-test/tests/warp/setelem-inlining-bailout.js26
-rw-r--r--js/src/jit-test/tests/warp/small-inlinable-builtins.js10
-rw-r--r--js/src/jit-test/tests/warp/store-element-hole-negative-index.js24
-rw-r--r--js/src/jit-test/tests/warp/store-element-hole-sparse-element.js24
-rw-r--r--js/src/jit-test/tests/warp/string-char.js15
-rw-r--r--js/src/jit-test/tests/warp/string-charCodeAt-constant-index-in-left-rope-child.js18
-rw-r--r--js/src/jit-test/tests/warp/string-compare-char-in-bounds.js24
-rw-r--r--js/src/jit-test/tests/warp/string-compare-char-out-of-bounds.js28
-rw-r--r--js/src/jit-test/tests/warp/string-compare-char-string.js60
-rw-r--r--js/src/jit-test/tests/warp/string-endswith-constant-string.js81
-rw-r--r--js/src/jit-test/tests/warp/string-indexof-constant-string-length-one.js74
-rw-r--r--js/src/jit-test/tests/warp/string-indexof-constant-string-length-two.js74
-rw-r--r--js/src/jit-test/tests/warp/string-indexof-constant-string.js98
-rw-r--r--js/src/jit-test/tests/warp/string-indexof-is-startswith.js29
-rw-r--r--js/src/jit-test/tests/warp/string-startswith-constant-string.js81
-rw-r--r--js/src/jit-test/tests/warp/string-substring-is-charat.js13
-rw-r--r--js/src/jit-test/tests/warp/string-substring-startswith-constant-string.js90
-rw-r--r--js/src/jit-test/tests/warp/string-substring-static-strings.js20
-rw-r--r--js/src/jit-test/tests/warp/string-tolowercase-latin1.js85
-rw-r--r--js/src/jit-test/tests/warp/string-totitlecase.js85
-rw-r--r--js/src/jit-test/tests/warp/string-trim.js75
-rw-r--r--js/src/jit-test/tests/warp/stub-folding-add-case.js35
-rw-r--r--js/src/jit-test/tests/warp/stub-folding-cross-compartment.js15
-rw-r--r--js/src/jit-test/tests/warp/stub-folding-transition.js24
-rw-r--r--js/src/jit-test/tests/warp/stub-folding.js35
-rw-r--r--js/src/jit-test/tests/warp/super-native-newtarget.js20
-rw-r--r--js/src/jit-test/tests/warp/throw-exception-stack-location.js41
-rw-r--r--js/src/jit-test/tests/warp/trial-inline-gc-1.js14
-rw-r--r--js/src/jit-test/tests/warp/trial-inline-gc-2.js18
-rw-r--r--js/src/jit-test/tests/warp/trial-inline-gc-3.js23
-rw-r--r--js/src/jit-test/tests/warp/try-catch-unwind.js29
-rw-r--r--js/src/jit-test/tests/warp/try-finally-1.js36
-rw-r--r--js/src/jit-test/tests/warp/try-finally-2.js42
-rw-r--r--js/src/jit-test/tests/warp/try-finally-3.js35
-rw-r--r--js/src/jit-test/tests/warp/try-finally-unwind.js28
-rw-r--r--js/src/jit-test/tests/warp/typedarray-element-exists.js46
-rw-r--r--js/src/jit-test/tests/warp/typedarrayindextoint32.js10
-rw-r--r--js/src/jit-test/tests/warp/typeof-is.js126
-rw-r--r--js/src/jit-test/tests/warp/typeof-switch.js78
196 files changed, 8653 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/bailout-inline-fun-call-no-args.js b/js/src/jit-test/tests/warp/bailout-inline-fun-call-no-args.js
new file mode 100644
index 0000000000..be888552e6
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bailout-inline-fun-call-no-args.js
@@ -0,0 +1,19 @@
+// |jit-test| --fast-warmup; --no-threads
+
+var iter = 0;
+
+function foo() {
+ var x = iter;
+ bailout();
+ return x;
+}
+
+function bar(x) {
+ return foo.call();
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ iter = i;
+ assertEq(bar(), i);
+}
diff --git a/js/src/jit-test/tests/warp/bailout-inline-getter.js b/js/src/jit-test/tests/warp/bailout-inline-getter.js
new file mode 100644
index 0000000000..86ef1300ec
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bailout-inline-getter.js
@@ -0,0 +1,22 @@
+// |jit-test| --fast-warmup; --no-threads
+
+var iter = 0;
+
+class A {
+ get foo() {
+ var x = iter;
+ bailout();
+ return x;
+ }
+}
+
+var a = new A();
+function bar() {
+ return a.foo;
+}
+
+with ({}) {}
+for(var i = 0; i < 100; i++) {
+ iter = i;
+ assertEq(bar(), i);
+}
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..3c74c3f612
--- /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; --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..63a0defe4e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1683309.js
@@ -0,0 +1,19 @@
+// |jit-test| slow; --ion-offthread-compile=off;
+//
+// Bug 1683309: Assertion failure: [barrier verifier] Unmarked edge: JS Object 0xebbb6d1dee0 'object slot' edge to JS Object 0xebbb6d29f60, at gc/Verifier.cpp:392
+
+
+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..25317b1d8d
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1683535-2.js
@@ -0,0 +1,12 @@
+function foo(y) {
+ return (1 >>> y >> 12) % 1.5 >>> 0
+}
+function bar(y) {
+ return (1 >>> y >> 12) / 1.5 >>> 0
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo(1);
+ bar(1);
+}
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..1d0a4724ff
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1683614.js
@@ -0,0 +1,13 @@
+// |jit-test| --ion-offthread-compile=off; --ion-full-warmup-threshold=0; --baseline-eager; skip-if: !this.hasOwnProperty("ReadableStream")
+
+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/bug1688136.js b/js/src/jit-test/tests/warp/bug1688136.js
new file mode 100644
index 0000000000..a7cf52097f
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1688136.js
@@ -0,0 +1,13 @@
+function f(a, c) {
+ if (c) {
+ a++;
+ } else {
+ a--;
+ }
+ return (a + a) | 0;
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ f(2147483647, i % 2);
+}
diff --git a/js/src/jit-test/tests/warp/bug1688346.js b/js/src/jit-test/tests/warp/bug1688346.js
new file mode 100644
index 0000000000..0e3bbcdd05
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1688346.js
@@ -0,0 +1,12 @@
+function f(x) {
+ let y = Math.trunc(x);
+ return y - y;
+}
+
+with ({}) {}
+
+for (var i = 0; i < 50; i++) {
+ f(0.1);
+}
+
+assertEq(f(NaN), NaN);
diff --git a/js/src/jit-test/tests/warp/bug1692857.js b/js/src/jit-test/tests/warp/bug1692857.js
new file mode 100644
index 0000000000..0da271200a
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1692857.js
@@ -0,0 +1,14 @@
+var arrayView = new Float32Array(new ArrayBuffer(40));
+
+function foo() {
+ var x = arrayView[0];
+ if (!x) {
+ x = arrayView[NaN];
+ }
+ return x;
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo();
+}
diff --git a/js/src/jit-test/tests/warp/bug1693062-01.js b/js/src/jit-test/tests/warp/bug1693062-01.js
new file mode 100644
index 0000000000..d670360d31
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1693062-01.js
@@ -0,0 +1,14 @@
+function foo(trigger) {
+ var x = Math.fround(1.5);
+ var a = Math.sqrt(2**53);
+ if (trigger) {
+ x = a + 1;
+ }
+ return x;
+}
+
+with ({}) {}
+for (var i = 0; i < 40; i++) {
+ foo(false);
+}
+assertEq(foo(true), Math.sqrt(2**53) + 1);
diff --git a/js/src/jit-test/tests/warp/bug1693062-02.js b/js/src/jit-test/tests/warp/bug1693062-02.js
new file mode 100644
index 0000000000..473b68773b
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1693062-02.js
@@ -0,0 +1,16 @@
+function foo(x) {
+ var result;
+ if (x) {
+ result = Math.fround(~x);
+ } else {
+ var temp = Math.sqrt(2**53);
+ for (var i = 0; i < 1000; i++) {} // Trigger OSR
+ result = temp + 1;
+ }
+ return result;
+}
+
+foo(true);
+for (var i = 0; i < 10; i++) {
+ assertEq(foo(false), Math.sqrt(2**53) + 1);
+}
diff --git a/js/src/jit-test/tests/warp/bug1694600.js b/js/src/jit-test/tests/warp/bug1694600.js
new file mode 100644
index 0000000000..7a0ee06343
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1694600.js
@@ -0,0 +1,16 @@
+function foo(fn, y) {
+ var dummy = y > 0 ? 1 : 2;
+ fn();
+ return y * y;
+}
+
+function nop() {}
+function throws() { throw 1; }
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo(nop, 0)
+ try {
+ foo(throws, 0x7fffffff)
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/warp/bug1696897.js b/js/src/jit-test/tests/warp/bug1696897.js
new file mode 100644
index 0000000000..fbdd671573
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1696897.js
@@ -0,0 +1,8 @@
+function foo() {
+ Object.defineProperty(this, "valueOf", ({value: 0, writable: true}));
+ Object.freeze(this);
+}
+
+for (var i = 0; i < 100; i++) {
+ foo.call('');
+}
diff --git a/js/src/jit-test/tests/warp/bug1697451.js b/js/src/jit-test/tests/warp/bug1697451.js
new file mode 100644
index 0000000000..9e8c166c99
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1697451.js
@@ -0,0 +1,15 @@
+var dummy;
+function foo(a) {
+ dummy = arguments.length;
+ return a;
+}
+
+function bar(x) {
+ var arg = x ? 1 : 2;
+ assertEq(foo(arg), arg);
+}
+
+with({}) {}
+for (var i = 0; i < 50; i++) {
+ bar(i % 2 == 0);
+}
diff --git a/js/src/jit-test/tests/warp/bug1697483.js b/js/src/jit-test/tests/warp/bug1697483.js
new file mode 100644
index 0000000000..189029ab14
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1697483.js
@@ -0,0 +1,46 @@
+function baz() { with({}) {}}
+
+function foo(a,b,c) {
+ var x1 = a + b;
+ var y1 = b + c;
+ var z1 = a + c;
+ var x2 = a * b;
+ var y2 = b * c;
+ var z2 = a * c;
+ var x3 = a - b;
+ var y3 = b - c;
+ var z3 = a - c;
+ var x4 = b - a;
+ var y4 = c - b;
+ var z4 = c - a;
+ var x1b = 1 + a + b;
+ var y1b = 1 + b + c;
+ var z1b = 1 + a + c;
+ var x2b = 1 + a * b;
+ var y2b = 1 + b * c;
+ var z2b = 1 + a * c;
+ var x3b = 1 + a - b;
+ var y3b = 1 + b - c;
+ var z3b = 1 + a - c;
+ var x4b = 1 + b - a;
+ var y4b = 1 + c - b;
+ var z4b = 1 + c - a;
+
+ var arg = arguments[a];
+
+ baz(x1, y1, z1, x1b, y1b, z1b,
+ x2, y2, z2, x2b, y2b, z2b,
+ x3, y3, z3, x3b, y3b, z3b,
+ x4, y4, z4, x4b, y4b, z4b);
+
+ return arg;
+}
+
+function bar(a,b,c) {
+ return foo(a+b,b+c,c+a);
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ bar(0,0,0)
+}
diff --git a/js/src/jit-test/tests/warp/bug1698126.js b/js/src/jit-test/tests/warp/bug1698126.js
new file mode 100644
index 0000000000..d0576c2487
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1698126.js
@@ -0,0 +1,11 @@
+function bar() { return arguments[0]; }
+
+function foo() {
+ var arr = new Float32Array(10);
+ return bar(arr[0]);
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ assertEq(foo(), 0);
+}
diff --git a/js/src/jit-test/tests/warp/bug1698609.js b/js/src/jit-test/tests/warp/bug1698609.js
new file mode 100644
index 0000000000..d28297c7c6
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1698609.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: helperThreadCount() === 0; --code-coverage
+// Note: --code-coverage is a hack here to disable lazy parsing.
+
+var src = "function foo(x) { return /abc/.test(x) }";
+var job = offThreadCompileToStencil(src);
+var stencil = finishOffThreadStencil(job);
+var re = evalStencil(stencil);
+
+for (var i = 0; i < 200; i++) {
+ foo("abc");
+}
diff --git a/js/src/jit-test/tests/warp/bug1699056.js b/js/src/jit-test/tests/warp/bug1699056.js
new file mode 100644
index 0000000000..cb16a5ffc2
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1699056.js
@@ -0,0 +1,12 @@
+var a = '';
+var b = '';
+
+function foo() {
+ a += 'x';
+ b = b + a;
+}
+
+with ({}) {}
+for (var i = 0; i < 50000; i++) {
+ try { foo() } catch {}
+}
diff --git a/js/src/jit-test/tests/warp/bug1700579.js b/js/src/jit-test/tests/warp/bug1700579.js
new file mode 100644
index 0000000000..73048c3550
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1700579.js
@@ -0,0 +1,28 @@
+// |jit-test| --fast-warmup; --ion-offthread-compile=off
+
+function foo(y) {
+ var a = y - 1;
+
+ if (y) {}
+
+ return bar(a);
+}
+
+with ({}) {}
+var bar;
+function bar1 (x,y) {
+ "use strict";
+ return x | 0;
+}
+
+function bar2(x) {
+ return x;
+}
+
+bar = bar1;
+for (var i = 0; i < 100; i++) {
+ foo(1);
+}
+
+bar = bar2;
+assertEq(foo(-2147483648), -2147483649);
diff --git a/js/src/jit-test/tests/warp/bug1700616.js b/js/src/jit-test/tests/warp/bug1700616.js
new file mode 100644
index 0000000000..9bdd83c474
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1700616.js
@@ -0,0 +1,32 @@
+function dummy() { with ({}) {}}
+
+function foo() {
+ var a = 0;
+ var b = 1;
+ var c = 2;
+ var d = 3;
+
+ // This call will run before we enter jitcode and won't have IC
+ // data, so branch pruning will remove the path from the entry
+ // block to the OSR preheader.
+ dummy();
+
+ // We will OSR in this loop. Because there's no path from the
+ // function entry to the loop, the only information we have
+ // about a, b, c, and d is that they come from the OSR block.
+ for (var i = 0; i < 1000; i++) {
+
+ // Create a bunch of phis that only depend on OsrValues.
+ // These phis will be specialized to MIRType::Value.
+ a = i % 2 ? b : c;
+ b = i % 3 ? c : d;
+ c = i % 4 ? d : a;
+ d = i % 5 ? a : b;
+
+ // This phi will be optimistically specialized to
+ // MIRType::String and trigger a bailout.
+ dummy(i % 6 ? d : "");
+ }
+ return a;
+}
+foo();
diff --git a/js/src/jit-test/tests/warp/bug1701208.js b/js/src/jit-test/tests/warp/bug1701208.js
new file mode 100644
index 0000000000..184c8dbf24
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1701208.js
@@ -0,0 +1,18 @@
+// |jit-test| --fast-warmup; --no-threads
+
+function dummy() { with ({}) {} }
+
+function foo() {
+ dummy();
+ var x = [];
+ var y = [];
+ for (var i = 0; i < 10; i++) { }
+ for (var i = 0; i < 100; i++) {
+ var swap = x;
+ x = y;
+ y = swap;
+ }
+ return x;
+}
+
+foo();
diff --git a/js/src/jit-test/tests/warp/bug1702465.js b/js/src/jit-test/tests/warp/bug1702465.js
new file mode 100644
index 0000000000..5143b52ab7
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1702465.js
@@ -0,0 +1,14 @@
+function foo() {
+ if (arguments[0]) {
+ return arguments[1];
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo(true, 1);
+}
+
+for (var i = 0; i < 100; i++) {
+ foo(false);
+}
diff --git a/js/src/jit-test/tests/warp/bug1703766.js b/js/src/jit-test/tests/warp/bug1703766.js
new file mode 100644
index 0000000000..7c1f420e9b
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1703766.js
@@ -0,0 +1,212 @@
+function bar() { return 0;}
+
+function foo(x) {
+ switch (x) {
+ case 0: bar(); break;
+ case 1: bar(); break;
+ case 2: bar(); break;
+ case 3: bar(); break;
+ case 4: bar(); break;
+ case 5: bar(); break;
+ case 6: bar(); break;
+ case 7: bar(); break;
+ case 8: bar(); break;
+ case 9: bar(); break;
+ case 10: bar(); break;
+ case 11: bar(); break;
+ case 12: bar(); break;
+ case 13: bar(); break;
+ case 14: bar(); break;
+ case 15: bar(); break;
+ case 16: bar(); break;
+ case 17: bar(); break;
+ case 18: bar(); break;
+ case 19: bar(); break;
+ case 20: bar(); break;
+ case 21: bar(); break;
+ case 22: bar(); break;
+ case 23: bar(); break;
+ case 24: bar(); break;
+ case 25: bar(); break;
+ case 26: bar(); break;
+ case 27: bar(); break;
+ case 28: bar(); break;
+ case 29: bar(); break;
+ case 30: bar(); break;
+ case 31: bar(); break;
+ case 32: bar(); break;
+ case 33: bar(); break;
+ case 34: bar(); break;
+ case 35: bar(); break;
+ case 36: bar(); break;
+ case 37: bar(); break;
+ case 38: bar(); break;
+ case 39: bar(); break;
+ case 40: bar(); break;
+ case 41: bar(); break;
+ case 42: bar(); break;
+ case 43: bar(); break;
+ case 44: bar(); break;
+ case 45: bar(); break;
+ case 46: bar(); break;
+ case 47: bar(); break;
+ case 48: bar(); break;
+ case 49: bar(); break;
+ case 50: bar(); break;
+ case 51: bar(); break;
+ case 52: bar(); break;
+ case 53: bar(); break;
+ case 54: bar(); break;
+ case 55: bar(); break;
+ case 56: bar(); break;
+ case 57: bar(); break;
+ case 58: bar(); break;
+ case 59: bar(); break;
+ case 60: bar(); break;
+ case 61: bar(); break;
+ case 62: bar(); break;
+ case 63: bar(); break;
+ case 64: bar(); break;
+ case 65: bar(); break;
+ case 66: bar(); break;
+ case 67: bar(); break;
+ case 68: bar(); break;
+ case 69: bar(); break;
+ case 70: bar(); break;
+ case 71: bar(); break;
+ case 72: bar(); break;
+ case 73: bar(); break;
+ case 74: bar(); break;
+ case 75: bar(); break;
+ case 76: bar(); break;
+ case 77: bar(); break;
+ case 78: bar(); break;
+ case 79: bar(); break;
+ case 80: bar(); break;
+ case 81: bar(); break;
+ case 82: bar(); break;
+ case 83: bar(); break;
+ case 84: bar(); break;
+ case 85: bar(); break;
+ case 86: bar(); break;
+ case 87: bar(); break;
+ case 88: bar(); break;
+ case 89: bar(); break;
+ case 90: bar(); break;
+ case 91: bar(); break;
+ case 92: bar(); break;
+ case 93: bar(); break;
+ case 94: bar(); break;
+ case 95: bar(); break;
+ case 96: bar(); break;
+ case 97: bar(); break;
+ case 98: bar(); break;
+ case 99: bar(); break;
+ case 100: bar(); break;
+ case 101: bar(); break;
+ case 102: bar(); break;
+ case 103: bar(); break;
+ case 104: bar(); break;
+ case 105: bar(); break;
+ case 106: bar(); break;
+ case 107: bar(); break;
+ case 108: bar(); break;
+ case 109: bar(); break;
+ case 110: bar(); break;
+ case 111: bar(); break;
+ case 112: bar(); break;
+ case 113: bar(); break;
+ case 114: bar(); break;
+ case 115: bar(); break;
+ case 116: bar(); break;
+ case 117: bar(); break;
+ case 118: bar(); break;
+ case 119: bar(); break;
+ case 120: bar(); break;
+ case 121: bar(); break;
+ case 122: bar(); break;
+ case 123: bar(); break;
+ case 124: bar(); break;
+ case 125: bar(); break;
+ case 126: bar(); break;
+ case 127: bar(); break;
+ case 128: bar(); break;
+ case 129: bar(); break;
+ case 130: bar(); break;
+ case 131: bar(); break;
+ case 132: bar(); break;
+ case 133: bar(); break;
+ case 134: bar(); break;
+ case 135: bar(); break;
+ case 136: bar(); break;
+ case 137: bar(); break;
+ case 138: bar(); break;
+ case 139: bar(); break;
+ case 140: bar(); break;
+ case 141: bar(); break;
+ case 142: bar(); break;
+ case 143: bar(); break;
+ case 144: bar(); break;
+ case 145: bar(); break;
+ case 146: bar(); break;
+ case 147: bar(); break;
+ case 148: bar(); break;
+ case 149: bar(); break;
+ case 150: bar(); break;
+ case 151: bar(); break;
+ case 152: bar(); break;
+ case 153: bar(); break;
+ case 154: bar(); break;
+ case 155: bar(); break;
+ case 156: bar(); break;
+ case 157: bar(); break;
+ case 158: bar(); break;
+ case 159: bar(); break;
+ case 160: bar(); break;
+ case 161: bar(); break;
+ case 162: bar(); break;
+ case 163: bar(); break;
+ case 164: bar(); break;
+ case 165: bar(); break;
+ case 166: bar(); break;
+ case 167: bar(); break;
+ case 168: bar(); break;
+ case 169: bar(); break;
+ case 170: bar(); break;
+ case 171: bar(); break;
+ case 172: bar(); break;
+ case 173: bar(); break;
+ case 174: bar(); break;
+ case 175: bar(); break;
+ case 176: bar(); break;
+ case 177: bar(); break;
+ case 178: bar(); break;
+ case 179: bar(); break;
+ case 180: bar(); break;
+ case 181: bar(); break;
+ case 182: bar(); break;
+ case 183: bar(); break;
+ case 184: bar(); break;
+ case 185: bar(); break;
+ case 186: bar(); break;
+ case 187: bar(); break;
+ case 188: bar(); break;
+ case 189: bar(); break;
+ case 190: bar(); break;
+ case 191: bar(); break;
+ case 192: bar(); break;
+ case 193: bar(); break;
+ case 194: bar(); break;
+ case 195: bar(); break;
+ case 196: bar(); break;
+ case 197: bar(); break;
+ case 198: bar(); break;
+ case 199: bar(); break;
+ case 200: bar(); break;
+ }
+ return x;
+}
+
+for (var i = 0; i < 5000; i++) {
+ foo(1)
+}
diff --git a/js/src/jit-test/tests/warp/bug1703817.js b/js/src/jit-test/tests/warp/bug1703817.js
new file mode 100644
index 0000000000..d495e06e5b
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1703817.js
@@ -0,0 +1,9 @@
+function foo() {
+ new Float64Array(0x40000001)()
+}
+
+for (var i = 0; i < 100; i++) {
+ try {
+ foo();
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/warp/bug1704467.js b/js/src/jit-test/tests/warp/bug1704467.js
new file mode 100644
index 0000000000..6d9172a2ef
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1704467.js
@@ -0,0 +1,9 @@
+var always_true = true;
+function f() {
+ var obj = {x: 1};
+ for (var i = 0; i < 100; i++) {
+ var res = always_true ? obj : null;
+ assertEq(res, obj);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/warp/bug1706314.js b/js/src/jit-test/tests/warp/bug1706314.js
new file mode 100644
index 0000000000..210f827a2a
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1706314.js
@@ -0,0 +1,9 @@
+function f(a,b) {
+ var o1 = {x: 1};
+ var o2 = {x: 1};
+ for (var i = 0; i < 100; i++) {
+ var res = a ? (b ? o1 : o2) : null;
+ assertEq(res, o1);
+ }
+}
+f(true, true);
diff --git a/js/src/jit-test/tests/warp/bug1708839.js b/js/src/jit-test/tests/warp/bug1708839.js
new file mode 100644
index 0000000000..93d5c5622e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1708839.js
@@ -0,0 +1,16 @@
+function f(a) {
+ let m = new Uint32Array([-1]);
+ let h = m[0];
+ let r = m[0];
+ if (a) {
+ h = undefined;
+ r = 0xff;
+ }
+ return h > r;
+};
+
+assertEq(f(false), false);
+for (let i = 0; i < 100; ++i) {
+ f(true);
+}
+assertEq(f(false), false);
diff --git a/js/src/jit-test/tests/warp/bug1713579.js b/js/src/jit-test/tests/warp/bug1713579.js
new file mode 100644
index 0000000000..d6bb30573c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1713579.js
@@ -0,0 +1,8 @@
+function f() {
+ var i = 0;
+ while (i < (this.foo = this.foo ^ 123)) {
+ this.prop = 1;
+ }
+}
+new f();
+f();
diff --git a/js/src/jit-test/tests/warp/bug1716231.js b/js/src/jit-test/tests/warp/bug1716231.js
new file mode 100644
index 0000000000..df51c0b08f
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1716231.js
@@ -0,0 +1,25 @@
+// |jit-test| --fast-warmup; --ion-offthread-compile=off
+
+const too_big_for_float32 = 67109020;
+
+function call_with_no_ic_data() {}
+
+function foo() {
+ call_with_no_ic_data();
+
+ let x = too_big_for_float32;
+ let result;
+
+ // We OSR in this loop.
+ for (let i = 0; i < 100; i++) {
+ const float32 = Math.fround(0);
+
+ // Create a phi with one float32-typed input
+ // and one OSRValue input.
+ result = float32 || x;
+ }
+
+ return result;
+}
+
+assertEq(foo(), too_big_for_float32);
diff --git a/js/src/jit-test/tests/warp/bug1716931.js b/js/src/jit-test/tests/warp/bug1716931.js
new file mode 100644
index 0000000000..ae430f53ff
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1716931.js
@@ -0,0 +1,13 @@
+// |jit-test| --fast-warmup; --no-threads
+
+function* foo(x) {
+ yield* x;
+ assertEq(true, false); // Unreachable
+}
+
+for (var i = 0; i < 10; i++) {
+ var count = 0;
+ for (var o of foo(Array(50))) {
+ if (count++ > 40) break;
+ }
+} \ No newline at end of file
diff --git a/js/src/jit-test/tests/warp/bug1719884.js b/js/src/jit-test/tests/warp/bug1719884.js
new file mode 100644
index 0000000000..cfcdbd8a2e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1719884.js
@@ -0,0 +1,19 @@
+// |jit-test| --fast-warmup; --no-threads
+
+var always_true = true;
+var unused = 3;
+
+function foo(input, trigger) {
+ if (trigger) {
+ return Math.atan2(always_true ? Math.trunc(input ** -0x80000001)
+ : unused,
+ +input);
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo(1, i == 15);
+}
+
+assertEq(foo(-Infinity, true), -Math.PI);
diff --git a/js/src/jit-test/tests/warp/bug1720093-1.js b/js/src/jit-test/tests/warp/bug1720093-1.js
new file mode 100644
index 0000000000..8f2286c117
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1720093-1.js
@@ -0,0 +1,17 @@
+function main() {
+ let obj = {2294967295: 0, 2294967296: 1};
+ let x;
+
+ for (let i = 0; i < 110; i++) {
+ let c = 2294967296;
+ x = --c;
+ Math.fround(0);
+ }
+
+ try {
+ throw 1;
+ } catch {
+ assertEq(obj[x], 0);
+ }
+}
+main();
diff --git a/js/src/jit-test/tests/warp/bug1720093-2.js b/js/src/jit-test/tests/warp/bug1720093-2.js
new file mode 100644
index 0000000000..af66f6477c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1720093-2.js
@@ -0,0 +1,19 @@
+function throws() { with ({}) {} throw 1; }
+
+function main() {
+ let obj = {2294967295: 0, 2294967296: 1};
+ let x;
+
+ for (let i = 0; i < 110; i++) {
+ let c = 2294967296;
+ x = --c;
+ Math.fround(0);
+ }
+
+ try {
+ return throws()
+ } catch {
+ assertEq(obj[x], 0);
+ }
+}
+main();
diff --git a/js/src/jit-test/tests/warp/bug1732601.js b/js/src/jit-test/tests/warp/bug1732601.js
new file mode 100644
index 0000000000..af4d995cb2
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1732601.js
@@ -0,0 +1,27 @@
+// |jit-test| --fast-warmup
+
+function Mixin(Target) {
+ var c = class extends Target {};
+ Target.prototype.x = 1; // Add shadowing property to disable teleporting.
+ return c;
+}
+function MixinFoo(Target) {
+ var c = class extends Target {
+ get foo() { return 2; }
+ set foo(value) {}
+ };
+ Target.prototype.x = 1; // Add shadowing property to disable teleporting.
+ return c;
+}
+
+class Base {}
+class MyClass extends Mixin(Mixin(Mixin(Mixin(Mixin(Mixin(Mixin(Mixin(Mixin(Mixin(Mixin(MixinFoo(Base)))))))))))) {}
+
+function test() {
+ var instance = new MyClass();
+ assertEq(instance.x, 1);
+ for (var i = 0; i < 500; i++) {
+ assertEq(instance.foo, 2);
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/warp/bug1735157.js b/js/src/jit-test/tests/warp/bug1735157.js
new file mode 100644
index 0000000000..aedbe03f98
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1735157.js
@@ -0,0 +1,5 @@
+const options = ["", {}];
+let value = "";
+for (let i = 0; i < 10000; i++) {
+ value += options[(Math.random() < 0.01) | 0];
+}
diff --git a/js/src/jit-test/tests/warp/bug1738676.js b/js/src/jit-test/tests/warp/bug1738676.js
new file mode 100644
index 0000000000..0d9e8fa568
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1738676.js
@@ -0,0 +1,19 @@
+function verify(n) {
+ with ({}) {}
+ assertEq(n, -0);
+}
+
+function main() {
+ let x;
+ let arr = [];
+
+ let i = 0;
+ do {
+ x = (256 - i) * 0;
+ arr[x] = 0;
+ i++
+ } while (i < 512);
+ verify(x);
+}
+
+main();
diff --git a/js/src/jit-test/tests/warp/bug1741635-1.js b/js/src/jit-test/tests/warp/bug1741635-1.js
new file mode 100644
index 0000000000..bf966da433
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1741635-1.js
@@ -0,0 +1,11 @@
+function foo() {}
+
+var i;
+function c(x, ...rest) {
+ for (i = 0; i < rest.length; i++)
+ foo(rest[i])
+}
+
+for (var j = 0; j < 100; j++) {
+ c(0, 0);
+}
diff --git a/js/src/jit-test/tests/warp/bug1741635-2.js b/js/src/jit-test/tests/warp/bug1741635-2.js
new file mode 100644
index 0000000000..e16d77a139
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1741635-2.js
@@ -0,0 +1,11 @@
+// |jit-test| --ion-range-analysis=off
+
+function f(a, ...rest) {
+ return rest.length;
+}
+
+with ({});
+
+for (let i = 0; i < 1000; ++i) {
+ assertEq(f(), 0);
+}
diff --git a/js/src/jit-test/tests/warp/bug1745949.js b/js/src/jit-test/tests/warp/bug1745949.js
new file mode 100644
index 0000000000..0047afe7c5
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1745949.js
@@ -0,0 +1,11 @@
+function foo() {
+ let v;
+ let i = -2000;
+ do {
+ v = i * -1;
+ v += v;
+ i++;
+ } while (i < 1);
+ return Object.is(v, -0);
+}
+assertEq(foo(), true);
diff --git a/js/src/jit-test/tests/warp/bug1761947.js b/js/src/jit-test/tests/warp/bug1761947.js
new file mode 100644
index 0000000000..64d6406172
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1761947.js
@@ -0,0 +1,28 @@
+let trigger = false;
+
+function bar(x) {
+ with ({}) {}
+ if (trigger) {
+ gc(foo, "shrinking");
+ trigger = false;
+ }
+ return Object(x);
+}
+
+function foo() {
+ let result = undefined;
+ const arr = [8];
+ for (var i = 0; i < 10; i++) {
+ result = bar(...arr);
+ assertEq(Number(result), 8);
+ }
+ return result;
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo();
+}
+
+trigger = true;
+foo();
diff --git a/js/src/jit-test/tests/warp/bug1762769.js b/js/src/jit-test/tests/warp/bug1762769.js
new file mode 100644
index 0000000000..2fad563b08
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1762769.js
@@ -0,0 +1,8 @@
+try {
+ try {}
+ finally {
+ throw 1;
+ }
+} catch {}
+
+for (var i = 0; i < 1000; i++) {}
diff --git a/js/src/jit-test/tests/warp/bug1762770.js b/js/src/jit-test/tests/warp/bug1762770.js
new file mode 100644
index 0000000000..8f693b77e6
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1762770.js
@@ -0,0 +1,13 @@
+function* a() {
+ try {
+ yield 1;
+ } finally {
+ for (c = 0; c < 100; c++);
+ }
+}
+
+var b = a();
+b.next();
+let result = b.return(42);
+assertEq(result.value, 42);
+assertEq(result.done, true);
diff --git a/js/src/jit-test/tests/warp/bug1763012-1.js b/js/src/jit-test/tests/warp/bug1763012-1.js
new file mode 100644
index 0000000000..fc1ce2b5d6
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1763012-1.js
@@ -0,0 +1,19 @@
+// |jit-test| --fast-warmup; --no-threads
+
+function f() {
+ let counter = 0;
+ for (var i = 0; i < 8; i++) {
+ let x = i / i;
+ for (var j = 0; j < 6; j++) {
+ if (x === x) {
+ if (j > 6) {} else {}
+ } else {
+ counter += 1;
+ }
+ }
+ }
+ return counter;
+}
+
+f();
+assertEq(f(), 6);
diff --git a/js/src/jit-test/tests/warp/bug1763012-2.js b/js/src/jit-test/tests/warp/bug1763012-2.js
new file mode 100644
index 0000000000..4802e2eef2
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1763012-2.js
@@ -0,0 +1,19 @@
+// |jit-test| --fast-warmup; --no-threads
+
+function f() {
+ let counter = 0;
+ for (var i = 0; i < 8; i++) {
+ let x = 0 % i;
+ for (var j = 0; j < 6; j++) {
+ if (x === x) {
+ if (j > 6) {} else {}
+ } else {
+ counter += 1;
+ }
+ }
+ }
+ return counter;
+}
+
+f();
+assertEq(f(), 6);
diff --git a/js/src/jit-test/tests/warp/bug1767196.js b/js/src/jit-test/tests/warp/bug1767196.js
new file mode 100644
index 0000000000..03b530518e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1767196.js
@@ -0,0 +1,15 @@
+// |jit-test| --fast-warmup; --no-threads
+function foo() {}
+function main() {
+ let value = 0.1;
+ let result;
+ foo();
+
+ const neverTrue = false;
+ for (let i = 0; i < 100; i++) {
+ let f32 = Math.fround(i) && 0;
+ result = neverTrue ? f32 : value;
+ }
+ return result;
+}
+assertEq(main(), 0.1);
diff --git a/js/src/jit-test/tests/warp/bug1769410.js b/js/src/jit-test/tests/warp/bug1769410.js
new file mode 100644
index 0000000000..9d66d96e39
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1769410.js
@@ -0,0 +1,14 @@
+// |jit-test| --fast-warmup
+function f(x) {
+ var a = Math.fround(Math.fround(false));
+ var b = Math.min(a, x ? Math.fround(x) : Math.fround(x));
+ return b >>> 0;
+}
+function test() {
+ with (this) {} // Don't inline.
+ for (var i = 0; i < 100; i++) {
+ assertEq(f(Infinity), 0);
+ }
+ assertEq(f(-1), 4294967295);
+}
+test();
diff --git a/js/src/jit-test/tests/warp/bug1770904.js b/js/src/jit-test/tests/warp/bug1770904.js
new file mode 100644
index 0000000000..c664edfaa4
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1770904.js
@@ -0,0 +1,7 @@
+var N = 100;
+var f = Function("return 0" + "+arguments[0]".repeat(N));
+
+for (let i = 0; i < 10000; ++i) {
+ assertEq(f(1), N);
+}
+
diff --git a/js/src/jit-test/tests/warp/bug1789821.js b/js/src/jit-test/tests/warp/bug1789821.js
new file mode 100644
index 0000000000..b1d3ee9a54
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1789821.js
@@ -0,0 +1,14 @@
+function foo(x) {
+ Math.max(...[x]);
+}
+
+with ({}) {}
+for (let i = 0; i < 100; i++) {
+ foo(0);
+}
+
+let called = false;
+const evil = { valueOf: () => { called = true; } };
+foo(evil);
+
+assertEq(called, true);
diff --git a/js/src/jit-test/tests/warp/bug1825220.js b/js/src/jit-test/tests/warp/bug1825220.js
new file mode 100644
index 0000000000..11da1ef449
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1825220.js
@@ -0,0 +1,11 @@
+// |jit-test| --ion-pruning=off; --fast-warmup
+
+function foo() {
+ Date.prototype.toLocaleString()
+}
+
+for (var i = 0; i < 100; i++) {
+ try {
+ foo();
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/warp/bug1825408.js b/js/src/jit-test/tests/warp/bug1825408.js
new file mode 100644
index 0000000000..1f25817c29
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1825408.js
@@ -0,0 +1,20 @@
+// |jit-test| --no-threads; --fast-warmup; --ion-warmup-threshold=0
+
+function a(b = (b, !b)) {
+ () => b
+}
+for (var i = 0; i < 20; i++) {
+ a("");
+}
+
+try {
+ a();
+} catch {}
+
+function f(arr) {
+ for (var i = 0; i < 10; i++) {
+ a(arr.x);
+ }
+}
+f({y: 1, x:1});
+f({x:2});
diff --git a/js/src/jit-test/tests/warp/bug1852238.js b/js/src/jit-test/tests/warp/bug1852238.js
new file mode 100644
index 0000000000..eaf8eb8a95
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1852238.js
@@ -0,0 +1,14 @@
+// |jit-test| --fast-warmup
+(function() {
+ eval("");
+ (function() {
+ var arr = [];
+ var res = 0;
+ for (var i = 2; i < 50; i++) {
+ for (var j = 1; j < 10; j++) {
+ res = JSON;
+ }
+ }
+ return res;
+ })();
+})();
diff --git a/js/src/jit-test/tests/warp/bug1852398.js b/js/src/jit-test/tests/warp/bug1852398.js
new file mode 100644
index 0000000000..6e12d2a31d
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1852398.js
@@ -0,0 +1,27 @@
+// |jit-test| --no-threads
+
+function inner(obj, f) {
+ return obj.x + f();
+}
+
+function middle(obj, f) {
+ return inner(obj, f);
+}
+
+function outer(obj, f) {
+ return middle(obj, f);
+}
+
+var fs = [() => 1, () => 2];
+
+with ({}) {}
+for (var i = 0; i < 1500; i++) {
+ var obj = {x: 1};
+ obj["y" + i % 2] = 2;
+ outer(obj, fs[i % 2]);
+}
+for (var i = 0; i < 1500; i++) {
+ var obj = {x: 1};
+ obj["y" + (3 + (i % 10))] = 2;
+ outer(obj, fs[i % 2]);
+}
diff --git a/js/src/jit-test/tests/warp/bug1852702.js b/js/src/jit-test/tests/warp/bug1852702.js
new file mode 100644
index 0000000000..c0a5c7f3ca
--- /dev/null
+++ b/js/src/jit-test/tests/warp/bug1852702.js
@@ -0,0 +1,17 @@
+// |jit-test| --fast-warmup
+function main() {
+ var f = function(x) {
+ x.f = f;
+ var f2 = x.f;
+ f2[Symbol.toPrimitive] = null;
+ try {
+ new f2(0);
+ } catch (e) {}
+ for (var i = 0; i < 100; i++) { }
+ };
+ new f(f);
+}
+gczeal(2);
+for (var i = 0; i < 100; i++) {
+ main();
+}
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/compare-constant-string.js b/js/src/jit-test/tests/warp/compare-constant-string.js
new file mode 100644
index 0000000000..d7512c3171
--- /dev/null
+++ b/js/src/jit-test/tests/warp/compare-constant-string.js
@@ -0,0 +1,90 @@
+// Test case to cover constant string comparison.
+//
+// Equality comparison for short (≤32 characters), Latin-1 constant strings is
+// optimised during lowering.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+const operators = [
+ "==", "===", "!=", "!==",
+];
+
+for (let i = 1; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ // Same string as the input.
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Same length as the input, but a different string.
+ String.fromCodePoint(...codePoints.slice(1, i + 1)),
+
+ // Shorter string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i - 1)),
+
+ // Longer string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i + 1)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+
+ for (let codePoints of [ascii, latin1, twoByte]) {
+ let str = String.fromCodePoint(...codePoints.slice(0, i));
+
+ for (let op of operators) {
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x ${op} "${str}";
+ });
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let lhs = str ${op} "${str}";
+ if (lhs !== expected[idx]) throw new Error();
+
+ let rhs = "${str}" ${op} str;
+ if (rhs !== expected[idx]) throw new Error();
+ }
+ `);
+ fn(strings);
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/warp/compare-empty-string.js b/js/src/jit-test/tests/warp/compare-empty-string.js
new file mode 100644
index 0000000000..30cf03f874
--- /dev/null
+++ b/js/src/jit-test/tests/warp/compare-empty-string.js
@@ -0,0 +1,50 @@
+// Test case to cover empty string comparison folding.
+//
+// MCompare can fold comparison with an empty string constant and replace it
+// with |string.length <op> 0|.
+
+const strings = [
+ // Zero length string.
+ "",
+
+ // Uncommon zero length strings.
+ newString("", {external: true}),
+
+ // Latin-1 strings.
+ "a",
+ "ä",
+ "monkey",
+
+ // Two-byte strings.
+ "猿",
+ "🐒",
+ newString("monkey", {twoByte: true}),
+];
+
+const operators = [
+ "==", "===", "!=", "!==",
+ "<", "<=", ">=", ">",
+];
+
+for (let op of operators) {
+ let lhs = x => `${x} ${op} ""`;
+ let rhs = x => `"" ${op} ${x}`;
+
+ for (let input of [lhs, rhs]) {
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return ${input("x")};
+ });
+
+ for (let i = 0; i < 200; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+ let res = ${input("str")};
+ assertEq(res, expected[idx]);
+ }
+ `);
+ fn(strings);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/conditional-test-guard.js b/js/src/jit-test/tests/warp/conditional-test-guard.js
new file mode 100644
index 0000000000..cc2cca8660
--- /dev/null
+++ b/js/src/jit-test/tests/warp/conditional-test-guard.js
@@ -0,0 +1,12 @@
+function f(x) {
+ return (+(x || 1) ? 0 : x);
+}
+
+// Prevent top-level Ion-compilation, so we don't inline |f|.
+with ({});
+
+for (let i = 0; i < 100; ++i) {
+ assertEq(f(), 0);
+}
+
+assertEq(f("not-a-number"), "not-a-number");
diff --git a/js/src/jit-test/tests/warp/conditional-test-undefined-1.js b/js/src/jit-test/tests/warp/conditional-test-undefined-1.js
new file mode 100644
index 0000000000..382f3ca966
--- /dev/null
+++ b/js/src/jit-test/tests/warp/conditional-test-undefined-1.js
@@ -0,0 +1,22 @@
+function g(array) {
+ // 1. Absent properties return |undefined| from CacheIR.
+ // 2. Tests on |undefined| are changed to |false| in CacheIR.
+ //
+ // When Warp compiling the CacheIR ops, the first test will then happen on
+ // a boolean, whereas the phi still sees the original undefined value.
+ if (array.does_not_exist || array.slice) {
+ return 1;
+ }
+ return 0;
+}
+
+function f() {
+ var array = [];
+ var r = 0;
+ for (let i = 0; i < 100; ++i) {
+ r += g(array);
+ }
+ assertEq(r, 100);
+}
+
+for (let i = 0; i < 2; ++i) f(); \ No newline at end of file
diff --git a/js/src/jit-test/tests/warp/conditional-test-undefined-2.js b/js/src/jit-test/tests/warp/conditional-test-undefined-2.js
new file mode 100644
index 0000000000..b0dada6684
--- /dev/null
+++ b/js/src/jit-test/tests/warp/conditional-test-undefined-2.js
@@ -0,0 +1,22 @@
+function g(array) {
+ // 1. Absent properties return |undefined| from CacheIR.
+ // 2. Tests on |undefined| are changed to |false| in CacheIR.
+ //
+ // When Warp compiling the CacheIR ops, the first test will then happen on
+ // a boolean, whereas the phi still sees the original undefined value.
+ if (array.does_not_exist || array.does_not_exist_too || array.slice) {
+ return 1;
+ }
+ return 0;
+}
+
+function f() {
+ var array = [];
+ var r = 0;
+ for (let i = 0; i < 100; ++i) {
+ r += g(array);
+ }
+ assertEq(r, 100);
+}
+
+for (let i = 0; i < 2; ++i) f(); \ No newline at end of file
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/fun-call-not-inlined.js b/js/src/jit-test/tests/warp/fun-call-not-inlined.js
new file mode 100644
index 0000000000..3428167de5
--- /dev/null
+++ b/js/src/jit-test/tests/warp/fun-call-not-inlined.js
@@ -0,0 +1,29 @@
+// |jit-test| --fast-warmup
+
+// Call a scripted function instead of Function.prototype.call.
+function testScriptedAtFunCallOp() {
+ var f = function(x) {
+ if (x === 130) bailout();
+ return x;
+ };
+ f.call = f;
+
+ for (var i = 0; i < 150; i++) {
+ assertEq(f.call(i), i);
+ }
+}
+testScriptedAtFunCallOp();
+
+// Call Function.prototype.call instead of a "normal" function.
+function testFunCallAtNormalCallOp() {
+ var f = function(x) {
+ if (x === 130) bailout();
+ return x;
+ };
+ f.myCall = Function.prototype.call;
+
+ for (var i = 0; i < 150; i++) {
+ assertEq(f.myCall(null, i), i);
+ }
+}
+testFunCallAtNormalCallOp();
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/function-var-environment-inlined.js b/js/src/jit-test/tests/warp/function-var-environment-inlined.js
new file mode 100644
index 0000000000..f3b9c9b3b7
--- /dev/null
+++ b/js/src/jit-test/tests/warp/function-var-environment-inlined.js
@@ -0,0 +1,15 @@
+function defaultValue() { return 3; }
+
+function testCallee(p = defaultValue()) {
+ var q = p + 1;
+ return () => q + p;
+}
+function test() {
+ var res = 0;
+ for (var i = 0; i < 2000; i++) {
+ res += testCallee()();
+ res += testCallee(1)();
+ }
+ assertEq(res, 20000);
+}
+test();
diff --git a/js/src/jit-test/tests/warp/function-var-environment.js b/js/src/jit-test/tests/warp/function-var-environment.js
new file mode 100644
index 0000000000..dd23cb67a9
--- /dev/null
+++ b/js/src/jit-test/tests/warp/function-var-environment.js
@@ -0,0 +1,43 @@
+function defaultValue() { return 123; }
+
+// 2 environment objects: Call => Var. The lambda uses both of them.
+function testBasic(p = defaultValue()) {
+ for (var i = 0; i < 2000; i++) {}
+ return () => i + p;
+}
+assertEq(testBasic()(), 2123);
+
+function testBailout(p = defaultValue()) {
+ for (var i = 0; i < 2000; i++) {
+ if (i > 1950) {
+ bailout();
+ }
+ }
+ return () => i + p;
+}
+assertEq(testBailout()(), 2123);
+
+// 3 environment objects: Call => Var => Lexical. The lambda uses all of them.
+let escaped;
+function testVarAndLexical(p = defaultValue()) {
+ var q = p + 1;
+ let i = 0;
+ for (; i < 2000; i++) {
+ escaped = () => i + p + q;
+ }
+}
+testVarAndLexical();
+assertEq(escaped(), 2247);
+
+function testVarAndLexicalBailout(p = defaultValue()) {
+ var q = p + 1;
+ let i = 0;
+ for (; i < 2000; i++) {
+ escaped = () => i + p - q;
+ if (i > 1950) {
+ bailout();
+ }
+ }
+}
+testVarAndLexicalBailout();
+assertEq(escaped(), 1999);
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-specific-atom-with-short-atom.js b/js/src/jit-test/tests/warp/guard-specific-atom-with-short-atom.js
new file mode 100644
index 0000000000..84be75fbfd
--- /dev/null
+++ b/js/src/jit-test/tests/warp/guard-specific-atom-with-short-atom.js
@@ -0,0 +1,82 @@
+// Test case to cover constant atom guards.
+//
+// GuardSpecificAtom for short (≤32 characters) constant atoms is optimised.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+for (let i = 1; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ // Same string as the input.
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Same length as the input, but a different string.
+ String.fromCodePoint(...codePoints.slice(1, i + 1)),
+
+ // Shorter string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i - 1)),
+
+ // Longer string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i + 1)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+
+ // Must be small enough to transition to megamorphic ICs.
+ const stringsPerLoop = 4;
+
+ for (let codePoints of [ascii, latin1, twoByte]) {
+ let str = String.fromCodePoint(...codePoints.slice(0, i));
+
+ for (let i = 0; i < strings.length; i += stringsPerLoop) {
+ let fn = Function("strings", `
+ var obj = {["${str}"]: 0};
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str in obj;
+ let expected = str === "${str}";
+ if (actual !== expected) throw new Error();
+ }
+ `);
+ fn(strings.slice(i, stringsPerLoop));
+ }
+ }
+}
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..4abc06ebd5
--- /dev/null
+++ b/js/src/jit-test/tests/warp/inline-array-at.js
@@ -0,0 +1,15 @@
+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/inlined-accessor-exc-bailout.js b/js/src/jit-test/tests/warp/inlined-accessor-exc-bailout.js
new file mode 100644
index 0000000000..9346ac65bb
--- /dev/null
+++ b/js/src/jit-test/tests/warp/inlined-accessor-exc-bailout.js
@@ -0,0 +1,45 @@
+// |jit-test| --fast-warmup
+
+// Tests for exception bailout from inlined getter/setter.
+
+function throwingGetter() {
+ var o = {};
+ var count = 0;
+ Object.defineProperty(o, "getter", {get: function() {
+ if (count++ === 195) {
+ throw 1;
+ }
+ }});
+ var ex = null;
+ try {
+ for (var i = 0; i < 200; i++) {
+ o.getter;
+ }
+ } catch(e) {
+ ex = e;
+ }
+ assertEq(ex, 1);
+ assertEq(count, 196);
+}
+throwingGetter();
+
+function throwingSetter() {
+ var o = {};
+ var count = 0;
+ Object.defineProperty(o, "setter", {set: function(v) {
+ if (count++ === 195) {
+ throw 1;
+ }
+ }});
+ var ex = null;
+ try {
+ for (var i = 0; i < 200; i++) {
+ o.setter = i;
+ }
+ } catch(e) {
+ ex = e;
+ }
+ assertEq(ex, 1);
+ assertEq(count, 196);
+}
+throwingSetter();
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/map-get-bigint.js b/js/src/jit-test/tests/warp/map-get-bigint.js
new file mode 100644
index 0000000000..3b746fb81c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-get-bigint.js
@@ -0,0 +1,211 @@
+// Similar test as "cacheir/map-get-bigint.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testInlineDigitsSameSign_same_map);
+
+function testInlineDigitsDifferentSign_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testInlineDigitsDifferentSign_same_map);
+
+function testHeapDigitsSameSign_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testHeapDigitsSameSign_same_map);
+
+function testHeapDigitsDifferentSign_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testHeapDigitsDifferentSign_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testInlineDigitsSameSign_different_map(n) {
+ var xs = [1n, 2n];
+ var ys = [3n, 4n];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testInlineDigitsSameSign_different_map);
+
+function testInlineDigitsDifferentSign_different_map(n) {
+ var xs = [-1n, 2n];
+ var ys = [1n, -2n];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testInlineDigitsDifferentSign_different_map);
+
+function testHeapDigitsSameSign_different_map(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 map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testHeapDigitsSameSign_different_map);
+
+function testHeapDigitsDifferentSign_different_map(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 map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testHeapDigitsDifferentSign_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [1n, 2n];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/map-get-nongcthing.js b/js/src/jit-test/tests/warp/map-get-nongcthing.js
new file mode 100644
index 0000000000..3a0e0f7632
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-get-nongcthing.js
@@ -0,0 +1,343 @@
+// Similar test as "cacheir/map-get-nongcthing.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testInt32_same_map);
+
+function testDouble_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testDouble_same_map);
+
+function testZero_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N);
+}
+runTest(testZero_same_map);
+
+function testNaN_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N);
+}
+runTest(testNaN_same_map);
+
+function testUndefinedAndNull_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testUndefinedAndNull_same_map);
+
+function testBoolean_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testBoolean_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testInt32_different_map(n) {
+ var xs = [1, 2];
+ var ys = [3, 4];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testInt32_different_map);
+
+function testDouble_different_map(n) {
+ var xs = [Math.PI, Infinity];
+ var ys = [Math.E, -Infinity];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testDouble_different_map);
+
+function testZero_different_map(n) {
+ var xs = [0, -0];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap([0], n);
+ var map2 = createMap([0], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N);
+}
+runTest(testZero_different_map);
+
+function testNaN_different_map(n) {
+ var xs = [NaN, -NaN];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap([NaN], n);
+ var map2 = createMap([NaN], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N);
+}
+runTest(testNaN_different_map);
+
+function testUndefinedAndNull_different_map(n) {
+ var xs = [undefined, null];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testUndefinedAndNull_different_map);
+
+function testBoolean_different_map(n) {
+ var xs = [true, false];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testBoolean_different_map);
+
+// Test the alias information is correct.
+
+function testInt32_alias(n) {
+ var xs = [1, 2];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_alias);
+
+function testDouble_alias(n) {
+ var xs = [Math.PI, Infinity];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_alias);
+
+function testUndefinedAndNull_alias(n) {
+ var xs = [undefined, null];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_alias);
+
+function testBoolean_alias(n) {
+ var xs = [true, false];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_alias);
diff --git a/js/src/jit-test/tests/warp/map-get-object.js b/js/src/jit-test/tests/warp/map-get-object.js
new file mode 100644
index 0000000000..0c13b6e50a
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-get-object.js
@@ -0,0 +1,104 @@
+// Similar test as "cacheir/map-get-object.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(test_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function test_different_map(n) {
+ var xs = [{}, {}];
+ var ys = [{}, {}];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(test_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [{}, {}];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
+
+// And finally test that we don't actually support GVN for objects, because the
+// hash changes when moving an object.
+
+function testRekey() {
+ var map = new Map();
+ var c = 0;
+ var N = 100;
+ for (var i = 0; i < N; ++i) {
+ var k = {};
+ map.set(k, 1);
+
+ c += map.get(k);
+
+ minorgc();
+
+ c += map.get(k);
+ }
+
+ assertEq(c, N * 2);
+}
+testRekey();
diff --git a/js/src/jit-test/tests/warp/map-get-string.js b/js/src/jit-test/tests/warp/map-get-string.js
new file mode 100644
index 0000000000..265c1caf94
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-get-string.js
@@ -0,0 +1,162 @@
+// Similar test as "cacheir/map-get-string.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_different_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testConstant_different_map);
+
+function testComputed_different_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testComputed_different_map);
+
+function testRope_different_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testRope_different_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testConstant_different_map(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testConstant_different_map);
+
+function testComputed_different_map(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = 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 = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testComputed_different_map);
+
+function testRope_different_map(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs.map(x => x.repeat(100)), n);
+ var map2 = 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 = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(testRope_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = ["a", "b"];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/map-get-symbol.js b/js/src/jit-test/tests/warp/map-get-symbol.js
new file mode 100644
index 0000000000..358f79ef96
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-get-symbol.js
@@ -0,0 +1,82 @@
+// Similar test as "cacheir/map-get-symbol.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(test_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function test_different_map(n) {
+ var xs = [Symbol(), Symbol()];
+ var ys = [Symbol(), Symbol()];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, N + N / 2);
+}
+runTest(test_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [Symbol(), Symbol()];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/map-get-value.js b/js/src/jit-test/tests/warp/map-get-value.js
new file mode 100644
index 0000000000..525ee4fc9b
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-get-value.js
@@ -0,0 +1,104 @@
+// Similar test as "cacheir/map-get-value.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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;
+ var w = map.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, (8 * 9) / 2 * 8 * 2);
+}
+runTest(testPolymorphic_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testPolymorphic_different_map(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 15];
+ var v = map1.get(z);
+ if (v !== undefined) c += v;
+ var w = map2.get(z);
+ if (w !== undefined) c += w;
+ }
+ assertEq(c, (8 * 9) / 2 * 8 * 2);
+}
+runTest(testPolymorphic_different_map);
+
+// Test the alias information is correct.
+
+function testPolymorphic_alias(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var map = createMap([], n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, 1);
+ var v = map.get(x);
+
+ map.delete(x);
+ var w = map.get(x);
+
+ c += v;
+ assertEq(w, undefined);
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_alias);
+
+// And finally test that we don't actually support GVN for values, because the
+// hash changes when moving a value which holds an object.
+
+function testRekey() {
+ var map = new Map();
+ var c = 0;
+ var N = 100;
+ for (var i = 0; i < N; ++i) {
+ var k = (i & 1) ? {} : null;
+ map.set(k, 1);
+
+ c += map.get(k);
+
+ minorgc();
+
+ c += map.get(k);
+ }
+
+ assertEq(c, N * 2);
+}
+testRekey();
diff --git a/js/src/jit-test/tests/warp/map-has-bigint.js b/js/src/jit-test/tests/warp/map-has-bigint.js
new file mode 100644
index 0000000000..64fd52de55
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-has-bigint.js
@@ -0,0 +1,192 @@
+// Similar test as "cacheir/map-has-bigint.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsSameSign_same_map);
+
+function testInlineDigitsDifferentSign_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsDifferentSign_same_map);
+
+function testHeapDigitsSameSign_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsSameSign_same_map);
+
+function testHeapDigitsDifferentSign_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsDifferentSign_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testInlineDigitsSameSign_different_map(n) {
+ var xs = [1n, 2n];
+ var ys = [3n, 4n];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsSameSign_different_map);
+
+function testInlineDigitsDifferentSign_different_map(n) {
+ var xs = [-1n, 2n];
+ var ys = [1n, -2n];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsDifferentSign_different_map);
+
+function testHeapDigitsSameSign_different_map(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 map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsSameSign_different_map);
+
+function testHeapDigitsDifferentSign_different_map(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 map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsDifferentSign_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [1n, 2n];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/map-has-nongcthing.js b/js/src/jit-test/tests/warp/map-has-nongcthing.js
new file mode 100644
index 0000000000..808e1c95b0
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-has-nongcthing.js
@@ -0,0 +1,307 @@
+// Similar test as "cacheir/map-has-nongcthing.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_same_map);
+
+function testDouble_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_same_map);
+
+function testZero_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testZero_same_map);
+
+function testNaN_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testNaN_same_map);
+
+function testUndefinedAndNull_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_same_map);
+
+function testBoolean_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testInt32_different_map(n) {
+ var xs = [1, 2];
+ var ys = [3, 4];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_different_map);
+
+function testDouble_different_map(n) {
+ var xs = [Math.PI, Infinity];
+ var ys = [Math.E, -Infinity];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_different_map);
+
+function testZero_different_map(n) {
+ var xs = [0, -0];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap([0], n);
+ var map2 = createMap([0], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testZero_different_map);
+
+function testNaN_different_map(n) {
+ var xs = [NaN, -NaN];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap([NaN], n);
+ var map2 = createMap([NaN], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testNaN_different_map);
+
+function testUndefinedAndNull_different_map(n) {
+ var xs = [undefined, null];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_different_map);
+
+function testBoolean_different_map(n) {
+ var xs = [true, false];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_different_map);
+
+// Test the alias information is correct.
+
+function testInt32_alias(n) {
+ var xs = [1, 2];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_alias);
+
+function testDouble_alias(n) {
+ var xs = [Math.PI, Infinity];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_alias);
+
+function testUndefinedAndNull_alias(n) {
+ var xs = [undefined, null];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_alias);
+
+function testBoolean_alias(n) {
+ var xs = [true, false];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_alias);
diff --git a/js/src/jit-test/tests/warp/map-has-object.js b/js/src/jit-test/tests/warp/map-has-object.js
new file mode 100644
index 0000000000..189213d6d4
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-has-object.js
@@ -0,0 +1,97 @@
+// Similar test as "cacheir/map-has-object.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function test_different_map(n) {
+ var xs = [{}, {}];
+ var ys = [{}, {}];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [{}, {}];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
+
+// And finally test that we don't actually support GVN for objects, because the
+// hash changes when moving an object.
+
+function testRekey() {
+ var map = new Map();
+ var c = 0;
+ var N = 100;
+ for (var i = 0; i < N; ++i) {
+ var k = {};
+ map.set(k, i);
+
+ if (map.has(k)) c++;
+
+ minorgc();
+
+ if (map.has(k)) c++;
+ }
+
+ assertEq(c, N * 2);
+}
+testRekey();
diff --git a/js/src/jit-test/tests/warp/map-has-string.js b/js/src/jit-test/tests/warp/map-has-string.js
new file mode 100644
index 0000000000..62bca37d22
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-has-string.js
@@ -0,0 +1,147 @@
+// Similar test as "cacheir/map-has-string.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testConstant_same_map);
+
+function testComputed_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testComputed_same_map);
+
+function testRope_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testRope_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testConstant_different_map(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testConstant_different_map);
+
+function testComputed_different_map(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = 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 (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testComputed_different_map);
+
+function testRope_different_map(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs.map(x => x.repeat(100)), n);
+ var map2 = 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 (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testRope_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = ["a", "b"];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/map-has-symbol.js b/js/src/jit-test/tests/warp/map-has-symbol.js
new file mode 100644
index 0000000000..8dc8e96781
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-has-symbol.js
@@ -0,0 +1,75 @@
+// Similar test as "cacheir/map-has-symbol.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function test_different_map(n) {
+ var xs = [Symbol(), Symbol()];
+ var ys = [Symbol(), Symbol()];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_different_map);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [Symbol(), Symbol()];
+ var map = createMap([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/map-has-value.js b/js/src/jit-test/tests/warp/map-has-value.js
new file mode 100644
index 0000000000..b203b762e4
--- /dev/null
+++ b/js/src/jit-test/tests/warp/map-has-value.js
@@ -0,0 +1,97 @@
+// Similar test as "cacheir/map-has-value.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_map(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++;
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_same_map);
+
+// Duplicate the above tests, but this time use a different map.
+
+function testPolymorphic_different_map(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []];
+ var zs = [...xs, ...ys];
+ var map1 = createMap(xs, n);
+ var map2 = createMap(xs, n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 15];
+ if (map1.has(z)) c++;
+ if (map2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_different_map);
+
+// Test the alias information is correct.
+
+function testPolymorphic_alias(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var map = createMap([], n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 15];
+
+ map.set(x, x);
+ if (map.has(x)) c++;
+
+ map.delete(x);
+ if (map.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_alias);
+
+// And finally test that we don't actually support GVN for values, because the
+// hash changes when moving a value which holds an object.
+
+function testRekey() {
+ var map = new Map();
+ var c = 0;
+ var N = 100;
+ for (var i = 0; i < N; ++i) {
+ var k = (i & 1) ? {} : null;
+ map.set(k, 1);
+
+ if (map.has(k)) c++;
+
+ minorgc();
+
+ if (map.has(k)) c++;
+ }
+
+ assertEq(c, N * 2);
+}
+testRekey();
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/min-max-foldsTo-1.js b/js/src/jit-test/tests/warp/min-max-foldsTo-1.js
new file mode 100644
index 0000000000..accc6ea416
--- /dev/null
+++ b/js/src/jit-test/tests/warp/min-max-foldsTo-1.js
@@ -0,0 +1,47 @@
+with ({}); // Don't inline anything into the top-level script.
+
+function args() { return arguments; }
+
+for (let xs of [
+ // Array
+ [[], [1, 2, 3]],
+
+ // String
+ ["", "asdf"],
+
+ // ArrayBufferView
+ [new Int32Array(0), new Int32Array(10)],
+
+ // Arguments
+ [args(), args(1, 2, 3)],
+]) {
+ for (let cst of [0, -1]) {
+ // Fold `Math.min(length ≥ 0, constant ≤ 0)` to `constant`.
+ let min = Function("x", `return Math.min(x.length, ${cst})`);
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ assertEq(min(x), cst);
+ }
+
+ // Reverse operands.
+ min = Function("x", `return Math.min(${cst}, x.length)`);
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ assertEq(min(x), cst);
+ }
+
+ // Fold `Math.max(length ≥ 0, constant ≤ 0)` to `length`.
+ let max = Function("x", `return Math.max(x.length, ${cst})`);
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ assertEq(max(x), x.length);
+ }
+
+ // Reverse operands.
+ max = Function("x", `return Math.max(${cst}, x.length)`);
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ assertEq(max(x), x.length);
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/warp/min-max-foldsTo-2.js b/js/src/jit-test/tests/warp/min-max-foldsTo-2.js
new file mode 100644
index 0000000000..f848ba824c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/min-max-foldsTo-2.js
@@ -0,0 +1,153 @@
+with ({}); // Don't inline anything into the top-level script.
+
+// Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
+for (let x of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let y of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let z of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ let fn = Function("z", `return Math.min(${x}, Math.min(${y}, z))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(x, Math.min(y, z)));
+ assertEq(r, Math.min(Math.min(x, y), z));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.min(${x}, Math.min(z, ${y}))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(x, Math.min(z, y)));
+ assertEq(r, Math.min(Math.min(x, y), z));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.min(Math.min(${y}, z), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(Math.min(y, z), x));
+ assertEq(r, Math.min(Math.min(x, y), z));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.min(Math.min(z, ${y}), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(Math.min(z, y), x));
+ assertEq(r, Math.min(Math.min(x, y), z));
+ }
+ }
+ }
+}
+
+// Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
+for (let x of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let y of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let z of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ let fn = Function("z", `return Math.max(${x}, Math.max(${y}, z))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(x, Math.max(y, z)));
+ assertEq(r, Math.max(Math.max(x, y), z));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.max(${x}, Math.max(z, ${y}))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(x, Math.max(z, y)));
+ assertEq(r, Math.max(Math.max(x, y), z));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.max(Math.max(${y}, z), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(Math.max(y, z), x));
+ assertEq(r, Math.max(Math.max(x, y), z));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.max(Math.max(z, ${y}), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(Math.max(z, y), x));
+ assertEq(r, Math.max(Math.max(x, y), z));
+ }
+ }
+ }
+}
+
+// Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
+for (let x of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let y of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let z of ["", "asdf", [], [1,2,3], new Int32Array(0), new Int32Array(10)]) {
+ let fn = Function("z", `return Math.min(${x}, Math.max(${y}, z.length))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(x, Math.max(y, z.length)));
+ assertEq(r, Math.max(Math.min(x, y), Math.min(x, z.length)));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.min(${x}, Math.max(z.length, ${y}))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(x, Math.max(z.length, y)));
+ assertEq(r, Math.max(Math.min(x, y), Math.min(x, z.length)));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.min(Math.max(${y}, z.length), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(Math.max(y, z.length), x));
+ assertEq(r, Math.max(Math.min(x, y), Math.min(x, z.length)));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.min(Math.max(z.length, ${y}), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.min(Math.max(z.length, y), x));
+ assertEq(r, Math.max(Math.min(x, y), Math.min(x, z.length)));
+ }
+ }
+ }
+}
+
+// Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
+for (let x of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let y of [-Infinity, -10, 0, 10, Infinity, NaN]) {
+ for (let z of ["", "asdf", [], [1,2,3], new Int32Array(0), new Int32Array(10)]) {
+ let fn = Function("z", `return Math.max(${x}, Math.min(${y}, z.length))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(x, Math.min(y, z.length)));
+ assertEq(r, Math.min(Math.max(x, y), Math.max(x, z.length)));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.max(${x}, Math.min(z.length, ${y}))`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(x, Math.min(z.length, y)));
+ assertEq(r, Math.min(Math.max(x, y), Math.max(x, z.length)));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.max(Math.min(${y}, z.length), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(Math.min(y, z.length), x));
+ assertEq(r, Math.min(Math.max(x, y), Math.max(x, z.length)));
+ }
+
+ // Reverse operands.
+ fn = Function("z", `return Math.max(Math.min(z.length, ${y}), ${x})`);
+ for (let i = 0; i < 100; ++i) {
+ let r = fn(z);
+ assertEq(r, Math.max(Math.min(z.length, y), x));
+ assertEq(r, Math.min(Math.max(x, y), Math.max(x, z.length)));
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/warp/min-max-foldsTo-3.js b/js/src/jit-test/tests/warp/min-max-foldsTo-3.js
new file mode 100644
index 0000000000..dd52ef2ffb
--- /dev/null
+++ b/js/src/jit-test/tests/warp/min-max-foldsTo-3.js
@@ -0,0 +1,177 @@
+with ({}); // Don't inline anything into the top-level script.
+
+const numbers = [
+ -Infinity, -10, -5, -2, -1, -0.5, 0, 0.5, 1, 2, 5, 10, Infinity, NaN,
+];
+
+// Test all supported types (Int32, Float32, Float64).
+const converters = [
+ x => `${x}`,
+ x => `(${x}|0)`,
+ x => `Math.fround(${x})`,
+ x => `numberToDouble(${x})`,
+];
+
+// Fold min(x, min(x, y)) to min(x, y).
+for (let cvt of converters) {
+ let x = cvt("x");
+ let y = cvt("y");
+ let c = Function("a", `return ${cvt("a")}`);
+
+ let min1 = Function("x, y", `return Math.min(${x}, Math.min(${x}, ${y}))`);
+ let min2 = Function("x, y", `return Math.min(${y}, Math.min(${x}, ${y}))`);
+ let min3 = Function("x, y", `return Math.min(Math.min(${x}, ${y}), ${x})`);
+ let min4 = Function("x, y", `return Math.min(Math.min(${x}, ${y}), ${y})`);
+
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < numbers.length; ++j) {
+ for (let k = 0; k < numbers.length; ++k) {
+ let x = numbers[j];
+ let y = numbers[k]
+ let r1 = min1(x, y);
+ let r2 = min2(x, y);
+ let r3 = min3(x, y);
+ let r4 = min4(x, y);
+
+ // Convert to the correct type before computing the expected results.
+ x = c(x);
+ y = c(y);
+
+ assertEq(r1, Math.min(x, Math.min(x, y)));
+ assertEq(r1, Math.min(x, y));
+
+ assertEq(r2, Math.min(y, Math.min(x, y)));
+ assertEq(r2, Math.min(x, y));
+
+ assertEq(r3, Math.min(Math.min(x, y), x));
+ assertEq(r3, Math.min(x, y));
+
+ assertEq(r4, Math.min(Math.min(x, y), y));
+ assertEq(r4, Math.min(x, y));
+ }
+ }
+ }
+}
+
+// Fold max(x, max(x, y)) to max(x, y).
+for (let cvt of converters) {
+ let x = cvt("x");
+ let y = cvt("y");
+ let c = Function("a", `return ${cvt("a")}`);
+
+ let max1 = Function("x, y", `return Math.max(${x}, Math.max(${x}, ${y}))`);
+ let max2 = Function("x, y", `return Math.max(${y}, Math.max(${x}, ${y}))`);
+ let max3 = Function("x, y", `return Math.max(Math.max(${x}, ${y}), ${x})`);
+ let max4 = Function("x, y", `return Math.max(Math.max(${x}, ${y}), ${y})`);
+
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < numbers.length; ++j) {
+ for (let k = 0; k < numbers.length; ++k) {
+ let x = numbers[j];
+ let y = numbers[k]
+ let r1 = max1(x, y);
+ let r2 = max2(x, y);
+ let r3 = max3(x, y);
+ let r4 = max4(x, y);
+
+ // Convert to the correct type before computing the expected results.
+ x = c(x);
+ y = c(y);
+
+ assertEq(r1, Math.max(x, Math.max(x, y)));
+ assertEq(r1, Math.max(x, y));
+
+ assertEq(r2, Math.max(y, Math.max(x, y)));
+ assertEq(r2, Math.max(x, y));
+
+ assertEq(r3, Math.max(Math.max(x, y), x));
+ assertEq(r3, Math.max(x, y));
+
+ assertEq(r4, Math.max(Math.max(x, y), y));
+ assertEq(r4, Math.max(x, y));
+ }
+ }
+ }
+}
+
+// Fold max(x, min(x, y)) = x.
+for (let cvt of converters) {
+ let x = cvt("x");
+ let y = cvt("y");
+ let c = Function("a", `return ${cvt("a")}`);
+
+ let maxmin1 = Function("x, y", `return Math.max(${x}, Math.min(${x}, ${y}))`);
+ let maxmin2 = Function("x, y", `return Math.max(${y}, Math.min(${x}, ${y}))`);
+ let maxmin3 = Function("x, y", `return Math.max(Math.min(${x}, ${y}), ${x})`);
+ let maxmin4 = Function("x, y", `return Math.max(Math.min(${x}, ${y}), ${y})`);
+
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < numbers.length; ++j) {
+ for (let k = 0; k < numbers.length; ++k) {
+ let x = numbers[j];
+ let y = numbers[k]
+ let r1 = maxmin1(x, y);
+ let r2 = maxmin2(x, y);
+ let r3 = maxmin3(x, y);
+ let r4 = maxmin4(x, y);
+
+ // Convert to the correct type before computing the expected results.
+ x = c(x);
+ y = c(y);
+
+ assertEq(r1, Math.max(x, Math.min(x, y)));
+ assertEq(r1, Number.isNaN(y) ? NaN : x);
+
+ assertEq(r2, Math.max(y, Math.min(x, y)));
+ assertEq(r2, Number.isNaN(x) ? NaN : y);
+
+ assertEq(r3, Math.max(Math.min(x, y), x));
+ assertEq(r3, Number.isNaN(y) ? NaN : x);
+
+ assertEq(r4, Math.max(Math.min(x, y), y));
+ assertEq(r4, Number.isNaN(x) ? NaN : y);
+ }
+ }
+ }
+}
+
+// Fold min(x, max(x, y)) = x.
+for (let cvt of converters) {
+ let x = cvt("x");
+ let y = cvt("y");
+ let c = Function("a", `return ${cvt("a")}`);
+
+ let minmax1 = Function("x, y", `return Math.min(${x}, Math.max(${x}, ${y}))`);
+ let minmax2 = Function("x, y", `return Math.min(${y}, Math.max(${x}, ${y}))`);
+ let minmax3 = Function("x, y", `return Math.min(Math.max(${x}, ${y}), ${x})`);
+ let minmax4 = Function("x, y", `return Math.min(Math.max(${x}, ${y}), ${y})`);
+
+ for (let i = 0; i < 20; ++i) {
+ for (let j = 0; j < numbers.length; ++j) {
+ for (let k = 0; k < numbers.length; ++k) {
+ let x = numbers[j];
+ let y = numbers[k]
+ let r1 = minmax1(x, y);
+ let r2 = minmax2(x, y);
+ let r3 = minmax3(x, y);
+ let r4 = minmax4(x, y);
+
+ // Convert to the correct type before computing the expected results.
+ x = c(x);
+ y = c(y);
+
+ assertEq(r1, Math.min(x, Math.max(x, y)));
+ assertEq(r1, Number.isNaN(y) ? NaN : x);
+
+ assertEq(r2, Math.min(y, Math.max(x, y)));
+ assertEq(r2, Number.isNaN(x) ? NaN : y);
+
+ assertEq(r3, Math.min(Math.max(x, y), x));
+ assertEq(r3, Number.isNaN(y) ? NaN : x);
+
+ assertEq(r4, Math.min(Math.max(x, y), y));
+ assertEq(r4, Number.isNaN(x) ? NaN : y);
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/warp/min-max-foldsTo-4.js b/js/src/jit-test/tests/warp/min-max-foldsTo-4.js
new file mode 100644
index 0000000000..aa7e09ba22
--- /dev/null
+++ b/js/src/jit-test/tests/warp/min-max-foldsTo-4.js
@@ -0,0 +1,11 @@
+with ({}); // Don't inline anything into the top-level script.
+
+function f(x) {
+ return Math.min(Math.max(x / x, x), x);
+}
+
+for (var i = 0; i < 100; ++i) {
+ f(1);
+}
+
+assertEq(f(0), NaN);
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/number-tostring-with-base-uppercase.js b/js/src/jit-test/tests/warp/number-tostring-with-base-uppercase.js
new file mode 100644
index 0000000000..4dd6b182a8
--- /dev/null
+++ b/js/src/jit-test/tests/warp/number-tostring-with-base-uppercase.js
@@ -0,0 +1,92 @@
+// Copy of "warp/number-tostring-with-base.js" with `toUpperCase()` conversion
+// after `Number.prototype.toString(base)`.
+
+function testConstantBaseFastPathTemplate(xs) {
+ assertEq(xs.length, $BASE * $BASE);
+ for (let j = 0; j < 200;) {
+ // The fast path can be used for all integers below |base * base|.
+ for (let i = 0; i < $BASE * $BASE; ++i, ++j) {
+ assertEq(i.toString($BASE).toUpperCase(), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a constant base argument
+// and the fast path for static strings can be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testConstantBaseFastPathTemplate}`.replaceAll("$BASE", base))();
+ let xs = Array.from({length: base * base}, (_, i) => i.toString(base).toUpperCase());
+ for (let i = 0; i < 2; ++i) {
+ fn(xs);
+ }
+}
+
+function testConstantBaseTemplate(xs) {
+ assertEq(xs.length, $BASE * $BASE * 2);
+ for (let j = 0; j < 200;) {
+ // The fast path can only be used for integers below |base * base|.
+ for (let i = 0; i < $BASE * $BASE * 2; ++i, ++j) {
+ assertEq(i.toString($BASE).toUpperCase(), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a constant base argument
+// and the fast path for static strings can't always be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testConstantBaseTemplate}`.replaceAll("$BASE", base))();
+ let xs = Array.from({length: base * base * 2}, (_, i) => i.toString(base).toUpperCase());
+ for (let i = 0; i < 2; ++i) {
+ fn(xs);
+ }
+}
+
+function testVariableBaseFastPathTemplate(xs, ys) {
+ assertEq(ys.length, 2);
+ assertEq(ys[0], ys[1]);
+ let base = ys[0];
+
+ assertEq(xs.length, base * base);
+
+ for (let j = 0; j < 200;) {
+ // The fast path can be used for all integers below |base * base|.
+ for (let i = 0; i < base * base; ++i, ++j) {
+ assertEq(i.toString(ys[i & 1]).toUpperCase(), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a non-constant base argument
+// and the fast path for static strings can be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testVariableBaseFastPathTemplate}`)();
+ let xs = Array.from({length: base * base}, (_, i) => i.toString(base).toUpperCase());
+ for (let i = 0; i < 2; ++i) {
+ fn(xs, [base, base]);
+ }
+}
+
+function testVariableBaseTemplate(xs, ys) {
+ assertEq(ys.length, 2);
+ assertEq(ys[0], ys[1]);
+ let base = ys[0];
+
+ assertEq(xs.length, base * base * 2);
+
+ for (let j = 0; j < 200;) {
+ // The fast path can only be used for integers below |base * base|.
+ for (let i = 0; i < base * base * 2; ++i, ++j) {
+ assertEq(i.toString(ys[i & 1]).toUpperCase(), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a non-constant base argument
+// and the fast path for static strings can't always be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testVariableBaseTemplate}`)();
+ let xs = Array.from({length: base * base * 2}, (_, i) => i.toString(base).toUpperCase());
+ for (let i = 0; i < 2; ++i) {
+ fn(xs, [base, base]);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/number-tostring-with-base.js b/js/src/jit-test/tests/warp/number-tostring-with-base.js
new file mode 100644
index 0000000000..47842bec3f
--- /dev/null
+++ b/js/src/jit-test/tests/warp/number-tostring-with-base.js
@@ -0,0 +1,89 @@
+function testConstantBaseFastPathTemplate(xs) {
+ assertEq(xs.length, $BASE * $BASE);
+ for (let j = 0; j < 200;) {
+ // The fast path can be used for all integers below |base * base|.
+ for (let i = 0; i < $BASE * $BASE; ++i, ++j) {
+ assertEq(i.toString($BASE), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a constant base argument
+// and the fast path for static strings can be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testConstantBaseFastPathTemplate}`.replaceAll("$BASE", base))();
+ let xs = Array.from({length: base * base}, (_, i) => i.toString(base));
+ for (let i = 0; i < 2; ++i) {
+ fn(xs);
+ }
+}
+
+function testConstantBaseTemplate(xs) {
+ assertEq(xs.length, $BASE * $BASE * 2);
+ for (let j = 0; j < 200;) {
+ // The fast path can only be used for integers below |base * base|.
+ for (let i = 0; i < $BASE * $BASE * 2; ++i, ++j) {
+ assertEq(i.toString($BASE), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a constant base argument
+// and the fast path for static strings can't always be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testConstantBaseTemplate}`.replaceAll("$BASE", base))();
+ let xs = Array.from({length: base * base * 2}, (_, i) => i.toString(base));
+ for (let i = 0; i < 2; ++i) {
+ fn(xs);
+ }
+}
+
+function testVariableBaseFastPathTemplate(xs, ys) {
+ assertEq(ys.length, 2);
+ assertEq(ys[0], ys[1]);
+ let base = ys[0];
+
+ assertEq(xs.length, base * base);
+
+ for (let j = 0; j < 200;) {
+ // The fast path can be used for all integers below |base * base|.
+ for (let i = 0; i < base * base; ++i, ++j) {
+ assertEq(i.toString(ys[i & 1]), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a non-constant base argument
+// and the fast path for static strings can be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testVariableBaseFastPathTemplate}`)();
+ let xs = Array.from({length: base * base}, (_, i) => i.toString(base));
+ for (let i = 0; i < 2; ++i) {
+ fn(xs, [base, base]);
+ }
+}
+
+function testVariableBaseTemplate(xs, ys) {
+ assertEq(ys.length, 2);
+ assertEq(ys[0], ys[1]);
+ let base = ys[0];
+
+ assertEq(xs.length, base * base * 2);
+
+ for (let j = 0; j < 200;) {
+ // The fast path can only be used for integers below |base * base|.
+ for (let i = 0; i < base * base * 2; ++i, ++j) {
+ assertEq(i.toString(ys[i & 1]), xs[i]);
+ }
+ }
+}
+
+// Test when Number.prototype.toString is called with a non-constant base argument
+// and the fast path for static strings can't always be used.
+for (let base = 2; base <= 36; ++base) {
+ let fn = Function(`return ${testVariableBaseTemplate}`)();
+ let xs = Array.from({length: base * base * 2}, (_, i) => i.toString(base));
+ for (let i = 0; i < 2; ++i) {
+ fn(xs, [base, base]);
+ }
+}
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/polymorphic-to-bool.js b/js/src/jit-test/tests/warp/polymorphic-to-bool.js
new file mode 100644
index 0000000000..adbfe2d791
--- /dev/null
+++ b/js/src/jit-test/tests/warp/polymorphic-to-bool.js
@@ -0,0 +1,39 @@
+// |jit-test| --fast-warmup; --no-threads
+
+function runTest(src, seen, last) {
+ with ({}) {}
+
+ // Make a fresh script.
+ var foo = eval(src);
+
+ // Compile it with polymorphic types.
+ for (var j = 0; j < 100; j++) {
+ foo(seen[j % seen.length]);
+ }
+
+ // Now test the type sorted last.
+ assertEq(foo(last), false);
+}
+
+function runTests(src) {
+ // Each of these |seen| sets will cause the |last| type to be
+ // the last type tested in testValueTruthyKernel.
+ runTest(src, [1n, Symbol("a"), 1.5, "", {} ], 1);
+ runTest(src, [1n, Symbol("a"), 1.5, "", true ], {});
+ runTest(src, [1n, Symbol("a"), 1.5, true, {} ], "a");
+ runTest(src, [1n, Symbol("a"), true, "", {} ], 1.5);
+ runTest(src, [1n, true, 1.5, "", {} ], Symbol("a"));
+ runTest(src, [true, Symbol("a"), 1.5, "", {} ], 1n);
+}
+
+// JumpIfFalse
+runTests("(x) => { if (x) { return false; }}");
+
+// And
+runTests("(x) => x && false");
+
+// Or
+runTests("(x) => !(x || true)");
+
+// Not
+runTests("(x) => !x");
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/rest-elements.js b/js/src/jit-test/tests/warp/rest-elements.js
new file mode 100644
index 0000000000..28525ad215
--- /dev/null
+++ b/js/src/jit-test/tests/warp/rest-elements.js
@@ -0,0 +1,40 @@
+// |jit-test| --ion-inlining=off; --fast-warmup
+function calleeWithFormals(a, b, ...arr) {
+ assertEq(b, 2);
+ if (arr.length > 0) {
+ assertEq(arr[0], 3);
+ }
+ if (arr.length > 1) {
+ assertEq(arr[1], Math);
+ }
+ if (arr.length > 2) {
+ assertEq(arr[2], "foo");
+ }
+ return arr;
+}
+function calleeWithoutFormals(...arr) {
+ if (arr.length > 0) {
+ assertEq(arr[0], 3);
+ }
+ if (arr.length > 1) {
+ assertEq(arr[1], Math);
+ }
+ if (arr.length > 2) {
+ assertEq(arr[2], "foo");
+ }
+ return arr;
+}
+function f() {
+ for (var i = 0; i < 100; i++) {
+ assertEq(calleeWithFormals(1, 2).length, 0);
+ assertEq(calleeWithFormals(1, 2, 3).length, 1);
+ assertEq(calleeWithFormals(1, 2, 3, Math).length, 2);
+ assertEq(calleeWithFormals(1, 2, 3, Math, "foo").length, 3);
+
+ assertEq(calleeWithoutFormals().length, 0);
+ assertEq(calleeWithoutFormals(3).length, 1);
+ assertEq(calleeWithoutFormals(3, Math).length, 2);
+ assertEq(calleeWithoutFormals(3, Math, "foo").length, 3);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-01.js b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-01.js
new file mode 100644
index 0000000000..0f88843615
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-01.js
@@ -0,0 +1,21 @@
+function escape(x) { with ({}) {} }
+
+function foo(...args) {
+ escape(args);
+ return bar.apply({}, args);
+}
+
+// |foo| must be small enough to be inlinable.
+assertEq(isSmallFunction(foo), true);
+
+function bar(x, y) {
+ return x + y;
+}
+
+with ({}) {}
+
+var sum = 0;
+for (var i = 0; i < 100; i++) {
+ sum += foo(1, 2);
+}
+assertEq(sum, 300);
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-02.js b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-02.js
new file mode 100644
index 0000000000..e0689189ba
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-02.js
@@ -0,0 +1,19 @@
+function foo(...args) {
+ args[0] = 3;
+ return bar.apply({}, args);
+}
+
+// |foo| must be small enough to be inlinable.
+assertEq(isSmallFunction(foo), true);
+
+function bar(x, y) {
+ return x + y;
+}
+
+with ({}) {}
+
+var sum = 0;
+for (var i = 0; i < 100; i++) {
+ sum += foo(1, 2);
+}
+assertEq(sum, 500);
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-03.js b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-03.js
new file mode 100644
index 0000000000..54b88b1bdc
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-03.js
@@ -0,0 +1,23 @@
+function escape() { with ({}) {} }
+
+function foo(i) {
+ return i;
+}
+
+function bar(n, ...args) {
+ escape(args);
+ return foo.apply({}, args);
+}
+
+// |bar| must be small enough to be inlinable.
+assertEq(isSmallFunction(bar), true);
+
+function baz(a, n) {
+ return bar(n, n);
+}
+
+var sum = 0;
+for (var i = 0; i < 10000; i++) {
+ sum += baz(0, 1);
+}
+assertEq(sum, 10000);
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-04.js b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-04.js
new file mode 100644
index 0000000000..e83b9946a3
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array-04.js
@@ -0,0 +1,24 @@
+var result;
+
+function g(a, b) {
+ with ({}) {}
+ result = a + b;
+}
+
+function escape() { with({}) {} }
+
+function f(...args) {
+ escape(args);
+ for (var i = 0; i < 50; ++i) {
+ g.apply(this, args);
+ }
+}
+
+// |f| must be small enough to be inlinable.
+assertEq(isSmallFunction(f), true);
+
+f(1, 2);
+assertEq(result, 3);
+
+f("");
+assertEq(result, "undefined");
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-apply-array.js b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array.js
new file mode 100644
index 0000000000..7b8d12bd06
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-apply-array.js
@@ -0,0 +1,43 @@
+// |jit-test| --fast-warmup; --no-threads
+
+setJitCompilerOption("baseline.warmup.trigger", 0);
+setJitCompilerOption("ion.warmup.trigger", 100);
+
+// Prevent GC from cancelling/discarding Ion compilations.
+gczeal(0);
+
+// Create a fresh set of functions for each argument count to avoid type pollution.
+function makeTest(count) {
+ var args = Array(count).fill(0).join(",");
+
+ return Function(`
+ function g() {
+ return arguments.length;
+ }
+
+ function f(...args) {
+ // When |f| is inlined into its caller, the number of arguments is fixed and
+ // we can scalar replace the inlined rest array.
+ assertRecoveredOnBailout(args, true);
+ return g(...args);
+ }
+
+ // |f| must be small enough to be inlined into the test function.
+ assertEq(isSmallFunction(f), true);
+
+ function test() {
+ for (let i = 0; i < 1000; ++i) {
+ assertEq(f(${args}), ${count});
+ }
+ }
+
+ return test;
+ `)();
+}
+
+// Limited by gc::CanUseFixedElementsForArray(), see also WarpBuilder::build_Rest().
+const maxRestArgs = 14;
+
+for (let i = 0; i <= maxRestArgs; ++i) {
+ makeTest(i)();
+}
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-01.js b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-01.js
new file mode 100644
index 0000000000..a68a9dad0e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-01.js
@@ -0,0 +1,28 @@
+setJitCompilerOption("inlining.bytecode-max-length", 200);
+
+function escape(x) { with ({}) {} }
+
+class Bar {
+ constructor(x, y) {
+ this.value = x + y;
+ }
+}
+
+class Foo extends Bar {
+ constructor(...args) {
+ escape(args);
+ super(...args);
+ }
+}
+
+// |Foo| must be small enough to be inlinable.
+assertEq(isSmallFunction(Foo), true);
+
+with ({}) {}
+
+var sum = 0;
+for (var i = 0; i < 100; i++) {
+ let obj = new Foo(1, 2);
+ sum += obj.value;
+}
+assertEq(sum, 300);
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-02.js b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-02.js
new file mode 100644
index 0000000000..06d44779c1
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-02.js
@@ -0,0 +1,26 @@
+setJitCompilerOption("inlining.bytecode-max-length", 200);
+
+class Bar {
+ constructor(x, y) {
+ this.value = x + y;
+ }
+}
+
+class Foo extends Bar {
+ constructor(...args) {
+ args[0] = 3;
+ super(...args);
+ }
+}
+
+// |Foo| must be small enough to be inlinable.
+assertEq(isSmallFunction(Foo), true);
+
+with ({}) {}
+
+var sum = 0;
+for (var i = 0; i < 100; i++) {
+ let obj = new Foo(1, 2);
+ sum += obj.value;
+}
+assertEq(sum, 500);
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-03.js b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-03.js
new file mode 100644
index 0000000000..72807b9f46
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-03.js
@@ -0,0 +1,32 @@
+setJitCompilerOption("inlining.bytecode-max-length", 200);
+
+function escape() { with ({}) {} }
+
+class Foo {
+ constructor(i) {
+ this.value = i;
+ }
+}
+
+class Bar extends Foo {
+ constructor(n, ...args) {
+ escape(args);
+ super(...args);
+ }
+}
+
+// |Bar| must be small enough to be inlinable.
+assertEq(isSmallFunction(Bar), true);
+
+class Baz extends Bar {
+ constructor(a, n) {
+ super(n, n);
+ }
+}
+
+var sum = 0;
+for (var i = 0; i < 10000; i++) {
+ let obj = new Baz(0, 1);
+ sum += obj.value;
+}
+assertEq(sum, 10000);
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-04.js b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-04.js
new file mode 100644
index 0000000000..290bbddcf7
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array-04.js
@@ -0,0 +1,26 @@
+setJitCompilerOption("inlining.bytecode-max-length", 200);
+
+var result;
+
+function g(a, b) {
+ with ({}) {}
+ result = a + b;
+}
+
+function escape() { with({}) {} }
+
+function f(...args) {
+ escape(args);
+ for (var i = 0; i < 50; ++i) {
+ new g(...args);
+ }
+}
+
+// |f| must be small enough to be inlinable.
+assertEq(isSmallFunction(f), true);
+
+f(1, 2);
+assertEq(result, 3);
+
+f("");
+assertEq(result, "undefined");
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-construct-array.js b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array.js
new file mode 100644
index 0000000000..cd3b02c4e5
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-construct-array.js
@@ -0,0 +1,48 @@
+// |jit-test| --fast-warmup; --no-threads
+
+setJitCompilerOption("baseline.warmup.trigger", 0);
+setJitCompilerOption("ion.warmup.trigger", 100);
+
+// Prevent GC from cancelling/discarding Ion compilations.
+gczeal(0);
+
+// Create a fresh set of functions for each argument count to avoid type pollution.
+function makeTest(count) {
+ var args = Array(count).fill(0).join(",");
+
+ return Function(`
+ class Base {
+ constructor() {
+ this.count = arguments.length;
+ }
+ }
+
+ class C extends Base {
+ constructor(...args) {
+ // When |C| is inlined into its caller, the number of arguments is fixed and
+ // we can scalar replace the inlined rest array.
+ assertRecoveredOnBailout(args, true);
+ return super(...args);
+ }
+ }
+
+ // |C| must be small enough to be inlined into the test function.
+ assertEq(isSmallFunction(C), true);
+
+ function test() {
+ for (let i = 0; i < 1000; ++i) {
+ let obj = new C(${args});
+ assertEq(obj.count, ${count});
+ }
+ }
+
+ return test;
+ `)();
+}
+
+// Limited by gc::CanUseFixedElementsForArray(), see also WarpBuilder::build_Rest().
+const maxRestArgs = 14;
+
+for (let i = 0; i <= maxRestArgs; ++i) {
+ makeTest(i)();
+}
diff --git a/js/src/jit-test/tests/warp/scalar-replace-array-iterator-next.js b/js/src/jit-test/tests/warp/scalar-replace-array-iterator-next.js
new file mode 100644
index 0000000000..d523bdb896
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-array-iterator-next.js
@@ -0,0 +1,24 @@
+//|jit-test| --ion-pruning=on
+
+// Verify that we can inline ArrayIteratorNext,
+// and scalar-replace the result object.
+
+if (getJitCompilerOptions()["ion.warmup.trigger"] <= 150)
+ setJitCompilerOption("ion.warmup.trigger", 150);
+
+gczeal(0);
+
+function foo(arr) {
+ var iterator = arr[Symbol.iterator]();
+ while (true) {
+ var result = iterator.next();
+ trialInline();
+ assertRecoveredOnBailout(result, true);
+ if (result.done) {
+ break;
+ }
+ }
+}
+
+with ({}) {}
+foo(Array(1000));
diff --git a/js/src/jit-test/tests/warp/scalar-replace-rest-apply-array.js b/js/src/jit-test/tests/warp/scalar-replace-rest-apply-array.js
new file mode 100644
index 0000000000..315dd58b5d
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-rest-apply-array.js
@@ -0,0 +1,38 @@
+// Prevent GC from cancelling/discarding Ion compilations.
+gczeal(0);
+
+// Create a fresh set of functions for each argument count to avoid type pollution.
+function makeTest(count) {
+ var args = Array(count).fill(0).join(",");
+
+ return Function(`
+ function g() {
+ return arguments.length;
+ }
+
+ function f(...args) {
+ assertRecoveredOnBailout(args, true);
+ return g(...args);
+ }
+
+ function test() {
+ // Ensure |f| isn't inlined into its caller.
+ with ({});
+
+ for (let i = 0; i < 1000; ++i) {
+ assertEq(f(${args}), ${count});
+ }
+ }
+
+ return test;
+ `)();
+}
+
+// Inline rest arguments are limited to 14 elements, cf.
+// gc::CanUseFixedElementsForArray() in WarpBuilder::build_Rest().
+// There isn't such limit when the function isn't inlined.
+const maxRestArgs = 20;
+
+for (let i = 0; i <= maxRestArgs; ++i) {
+ makeTest(i)();
+}
diff --git a/js/src/jit-test/tests/warp/scalar-replace-rest-construct-array.js b/js/src/jit-test/tests/warp/scalar-replace-rest-construct-array.js
new file mode 100644
index 0000000000..e2c0c5b354
--- /dev/null
+++ b/js/src/jit-test/tests/warp/scalar-replace-rest-construct-array.js
@@ -0,0 +1,43 @@
+// Prevent GC from cancelling/discarding Ion compilations.
+gczeal(0);
+
+// Create a fresh set of functions for each argument count to avoid type pollution.
+function makeTest(count) {
+ var args = Array(count).fill(0).join(",");
+
+ return Function(`
+ class Base {
+ constructor() {
+ this.count = arguments.length;
+ }
+ }
+
+ class C extends Base {
+ constructor(...args) {
+ assertRecoveredOnBailout(args, true);
+ return super(...args);
+ }
+ }
+
+ function test() {
+ // Ensure |C| isn't inlined into its caller.
+ with ({});
+
+ for (let i = 0; i < 1000; ++i) {
+ let obj = new C(${args});
+ assertEq(obj.count, ${count});
+ }
+ }
+
+ return test;
+ `)();
+}
+
+// Inline rest arguments are limited to 14 elements, cf.
+// gc::CanUseFixedElementsForArray() in WarpBuilder::build_Rest().
+// There isn't such limit when the function isn't inlined.
+const maxRestArgs = 20;
+
+for (let i = 0; i <= maxRestArgs; ++i) {
+ makeTest(i)();
+}
diff --git a/js/src/jit-test/tests/warp/set-has-bigint.js b/js/src/jit-test/tests/warp/set-has-bigint.js
new file mode 100644
index 0000000000..f282760eb2
--- /dev/null
+++ b/js/src/jit-test/tests/warp/set-has-bigint.js
@@ -0,0 +1,192 @@
+// Similar test as "cacheir/set-has-bigint.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsSameSign_same_set);
+
+function testInlineDigitsDifferentSign_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsDifferentSign_same_set);
+
+function testHeapDigitsSameSign_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsSameSign_same_set);
+
+function testHeapDigitsDifferentSign_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsDifferentSign_same_set);
+
+// Duplicate the above tests, but this time use a different set.
+
+function testInlineDigitsSameSign_different_set(n) {
+ var xs = [1n, 2n];
+ var ys = [3n, 4n];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsSameSign_different_set);
+
+function testInlineDigitsDifferentSign_different_set(n) {
+ var xs = [-1n, 2n];
+ var ys = [1n, -2n];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInlineDigitsDifferentSign_different_set);
+
+function testHeapDigitsSameSign_different_set(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 set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsSameSign_different_set);
+
+function testHeapDigitsDifferentSign_different_set(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 set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testHeapDigitsDifferentSign_different_set);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [1n, 2n];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/set-has-nongcthing.js b/js/src/jit-test/tests/warp/set-has-nongcthing.js
new file mode 100644
index 0000000000..845b9431b2
--- /dev/null
+++ b/js/src/jit-test/tests/warp/set-has-nongcthing.js
@@ -0,0 +1,307 @@
+// Similar test as "cacheir/set-has-nongcthing.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_same_set);
+
+function testDouble_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_same_set);
+
+function testZero_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testZero_same_set);
+
+function testNaN_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testNaN_same_set);
+
+function testUndefinedAndNull_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_same_set);
+
+function testBoolean_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_same_set);
+
+// Duplicate the above tests, but this time use a different set.
+
+function testInt32_different_set(n) {
+ var xs = [1, 2];
+ var ys = [3, 4];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_different_set);
+
+function testDouble_different_set(n) {
+ var xs = [Math.PI, Infinity];
+ var ys = [Math.E, -Infinity];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_different_set);
+
+function testZero_different_set(n) {
+ var xs = [0, -0];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set1 = createSet([0], n);
+ var set2 = createSet([0], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testZero_different_set);
+
+function testNaN_different_set(n) {
+ var xs = [NaN, -NaN];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set1 = createSet([NaN], n);
+ var set2 = createSet([NaN], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testNaN_different_set);
+
+function testUndefinedAndNull_different_set(n) {
+ var xs = [undefined, null];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_different_set);
+
+function testBoolean_different_set(n) {
+ var xs = [true, false];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_different_set);
+
+// Test the alias information is correct.
+
+function testInt32_alias(n) {
+ var xs = [1, 2];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testInt32_alias);
+
+function testDouble_alias(n) {
+ var xs = [Math.PI, Infinity];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testDouble_alias);
+
+function testUndefinedAndNull_alias(n) {
+ var xs = [undefined, null];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testUndefinedAndNull_alias);
+
+function testBoolean_alias(n) {
+ var xs = [true, false];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testBoolean_alias);
diff --git a/js/src/jit-test/tests/warp/set-has-object.js b/js/src/jit-test/tests/warp/set-has-object.js
new file mode 100644
index 0000000000..87cf85f791
--- /dev/null
+++ b/js/src/jit-test/tests/warp/set-has-object.js
@@ -0,0 +1,97 @@
+// Similar test as "cacheir/set-has-object.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_same_set);
+
+// Duplicate the above tests, but this time use a different set.
+
+function test_different_set(n) {
+ var xs = [{}, {}];
+ var ys = [{}, {}];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_different_set);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [{}, {}];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
+
+// And finally test that we don't actually support GVN for objects, because the
+// hash changes when moving an object.
+
+function testRekey() {
+ var set = new Set();
+ var c = 0;
+ var N = 100;
+ for (var i = 0; i < N; ++i) {
+ var k = {};
+ set.add(k);
+
+ if (set.has(k)) c++;
+
+ minorgc();
+
+ if (set.has(k)) c++;
+ }
+
+ assertEq(c, N * 2);
+}
+testRekey();
diff --git a/js/src/jit-test/tests/warp/set-has-string.js b/js/src/jit-test/tests/warp/set-has-string.js
new file mode 100644
index 0000000000..65442a4249
--- /dev/null
+++ b/js/src/jit-test/tests/warp/set-has-string.js
@@ -0,0 +1,147 @@
+// Similar test as "cacheir/set-has-string.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testConstant_same_set);
+
+function testComputed_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testComputed_same_set);
+
+function testRope_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testRope_same_set);
+
+// Duplicate the above tests, but this time use a different set.
+
+function testConstant_different_set(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testConstant_different_set);
+
+function testComputed_different_set(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = 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 (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testComputed_different_set);
+
+function testRope_different_set(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs.map(x => x.repeat(100)), n);
+ var set2 = 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 (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testRope_different_set);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = ["a", "b"];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/set-has-symbol.js b/js/src/jit-test/tests/warp/set-has-symbol.js
new file mode 100644
index 0000000000..0f1c697ca3
--- /dev/null
+++ b/js/src/jit-test/tests/warp/set-has-symbol.js
@@ -0,0 +1,75 @@
+// Similar test as "cacheir/set-has-symbol.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_same_set);
+
+// Duplicate the above tests, but this time use a different set.
+
+function test_different_set(n) {
+ var xs = [Symbol(), Symbol()];
+ var ys = [Symbol(), Symbol()];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_different_set);
+
+// Test the alias information is correct.
+
+function test_alias(n) {
+ var xs = [Symbol(), Symbol()];
+ var set = createSet([], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 1];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(test_alias);
diff --git a/js/src/jit-test/tests/warp/set-has-value.js b/js/src/jit-test/tests/warp/set-has-value.js
new file mode 100644
index 0000000000..4a6a8464e0
--- /dev/null
+++ b/js/src/jit-test/tests/warp/set-has-value.js
@@ -0,0 +1,97 @@
+// Similar test as "cacheir/set-has-value.js", except that we now perform
+// duplicate lookups to ensure GVN works properly.
+
+// 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_same_set(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++;
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_same_set);
+
+// Duplicate the above tests, but this time use a different set.
+
+function testPolymorphic_different_set(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []];
+ var zs = [...xs, ...ys];
+ var set1 = createSet(xs, n);
+ var set2 = createSet(xs, n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 15];
+ if (set1.has(z)) c++;
+ if (set2.has(z)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_different_set);
+
+// Test the alias information is correct.
+
+function testPolymorphic_alias(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var set = createSet([], n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var x = xs[i & 15];
+
+ set.add(x);
+ if (set.has(x)) c++;
+
+ set.delete(x);
+ if (set.has(x)) c++;
+ }
+ assertEq(c, N);
+}
+runTest(testPolymorphic_alias);
+
+// And finally test that we don't actually support GVN for values, because the
+// hash changes when moving a value which holds an object.
+
+function testRekey() {
+ var set = new Set();
+ var c = 0;
+ var N = 100;
+ for (var i = 0; i < N; ++i) {
+ var k = (i & 1) ? {} : null;
+ set.add(k);
+
+ if (set.has(k)) c++;
+
+ minorgc();
+
+ if (set.has(k)) c++;
+ }
+
+ assertEq(c, N * 2);
+}
+testRekey();
diff --git a/js/src/jit-test/tests/warp/setelem-inlining-bailout.js b/js/src/jit-test/tests/warp/setelem-inlining-bailout.js
new file mode 100644
index 0000000000..102b2d7dcc
--- /dev/null
+++ b/js/src/jit-test/tests/warp/setelem-inlining-bailout.js
@@ -0,0 +1,26 @@
+// |jit-test| --fast-warmup; --no-threads
+
+// Inlining setters for SetElem ops will require bailout changes.
+
+with ({}) { }
+var trigger = false;
+
+var obj = {
+ set f(x) {
+ if (trigger) {
+ bailout();
+ }
+ }
+};
+
+var sum = 0;
+function foo(x) {
+ for (var i = 0; i < 35; i++) {
+ var t = obj[x] = i;
+ sum += t;
+ trigger = i % 10 == 0;
+ }
+}
+
+foo("f");
+assertEq(sum, 595);
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/store-element-hole-negative-index.js b/js/src/jit-test/tests/warp/store-element-hole-negative-index.js
new file mode 100644
index 0000000000..43d017769c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/store-element-hole-negative-index.js
@@ -0,0 +1,24 @@
+function test() {
+ Object.defineProperty(Array.prototype, -1, {
+ set() {
+ throw new Error("shouldn't get here");
+ }
+ });
+
+ // A bunch of indices which grow the array.
+ var indices = [];
+ for (var i = 0; i < 10_000; ++i) indices.push(i);
+
+ // And finally a negative index.
+ indices.push(-1);
+
+ // Plain data properties use DefineDataProperty.
+ var desc = {value: 0, writable: true, enumerable: true, configurable: true};
+
+ var a = [];
+ for (var i = 0; i < indices.length; ++i) {
+ Object.defineProperty(a, indices[i], desc);
+ }
+}
+
+test();
diff --git a/js/src/jit-test/tests/warp/store-element-hole-sparse-element.js b/js/src/jit-test/tests/warp/store-element-hole-sparse-element.js
new file mode 100644
index 0000000000..2012404f51
--- /dev/null
+++ b/js/src/jit-test/tests/warp/store-element-hole-sparse-element.js
@@ -0,0 +1,24 @@
+function test() {
+ Object.defineProperty(Array.prototype, 10_000 * 50, {
+ set() {
+ throw new Error("shouldn't get here");
+ }
+ });
+
+ // A bunch of indices which grow the array.
+ var indices = [];
+ for (var i = 0; i < 10_000; ++i) indices.push(i);
+
+ // And finally an index for a sparse property.
+ indices.push(10_000 * 50);
+
+ // Plain data properties use DefineDataProperty.
+ var desc = {value: 0, writable: true, enumerable: true, configurable: true};
+
+ var a = [];
+ for (var i = 0; i < indices.length; ++i) {
+ Object.defineProperty(a, indices[i], desc);
+ }
+}
+
+test();
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/string-charCodeAt-constant-index-in-left-rope-child.js b/js/src/jit-test/tests/warp/string-charCodeAt-constant-index-in-left-rope-child.js
new file mode 100644
index 0000000000..5835b019ad
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-charCodeAt-constant-index-in-left-rope-child.js
@@ -0,0 +1,18 @@
+// `str.charCodeAt(0)` doesn't need to inspect the right rope child, because
+// the first character is guaranteed to be in the left child.
+
+const ropes = [
+ newRope("ABCDEFGHIJKL", "MNOPQRSTUVWXYZ"),
+ newRope("abcdefghijkl", "mnopqrstuvwxyz"),
+
+ newRope("A", "BCDEFGHIJKLMNOPQRSTUVWXYZ"),
+ newRope("a", "bcdefghijklmnopqrstuvwxyz"),
+];
+
+for (let i = 0; i < 500; ++i) {
+ let rope = ropes[i & 3];
+
+ let actual = rope.charCodeAt(0);
+ let expected = 0x41 + (i & 1) * 0x20;
+ assertEq(actual, expected);
+}
diff --git a/js/src/jit-test/tests/warp/string-compare-char-in-bounds.js b/js/src/jit-test/tests/warp/string-compare-char-in-bounds.js
new file mode 100644
index 0000000000..31c48a43fd
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-compare-char-in-bounds.js
@@ -0,0 +1,24 @@
+// |str.char(idx) == "b"| is compiled as |str.charCodeAt(idx) == 0x62|.
+
+const strings = [
+ "a", "b", "c",
+ "a-", "b-", "c-",
+];
+
+for (let i = 0; i < 1000; ++i) {
+ let str = strings[i % strings.length];
+
+ for (let j = 0; j < str.length; ++j) {
+ let ch = str.charAt(j);
+ let code = str.charCodeAt(j);
+
+ assertEq(ch == "b", code == 0x62);
+ assertEq(ch != "b", code != 0x62);
+
+ assertEq(ch < "b", code < 0x62);
+ assertEq(ch <= "b", code <= 0x62);
+
+ assertEq(ch > "b", code > 0x62);
+ assertEq(ch >= "b", code >= 0x62);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-compare-char-out-of-bounds.js b/js/src/jit-test/tests/warp/string-compare-char-out-of-bounds.js
new file mode 100644
index 0000000000..842b009a0f
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-compare-char-out-of-bounds.js
@@ -0,0 +1,28 @@
+// |str.char(idx) == "b"| is compiled as |str.charCodeAt(idx) == 0x62|.
+
+const strings = [
+ "",
+ "a", "b", "c",
+ "a-", "b-", "c-",
+];
+
+for (let i = 0; i < 1000; ++i) {
+ let str = strings[i % strings.length];
+
+ // |j <= str.length| to test out-of-bounds access, too.
+ for (let j = 0; j <= str.length; ++j) {
+ let ch = str.charAt(j);
+ let code = j < str.length ? str.charCodeAt(j) : -1;
+
+ assertEq(ch == "", code == -1);
+
+ assertEq(ch == "b", code == 0x62);
+ assertEq(ch != "b", code != 0x62);
+
+ assertEq(ch < "b", code < 0x62);
+ assertEq(ch <= "b", code <= 0x62);
+
+ assertEq(ch > "b", code > 0x62);
+ assertEq(ch >= "b", code >= 0x62);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-compare-char-string.js b/js/src/jit-test/tests/warp/string-compare-char-string.js
new file mode 100644
index 0000000000..f56009f5a1
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-compare-char-string.js
@@ -0,0 +1,60 @@
+// Test comparison against single-element character strings.
+
+function makeComparator(code) {
+ var str = String.fromCharCode(code);
+
+ return Function("ch", "code", `
+ assertEq(ch == "${str}", code == ${code} && ch.length == 1);
+ assertEq(ch != "${str}", code != ${code} || ch.length != 1);
+
+ assertEq(ch < "${str}", code < ${code} || (code == ${code} && ch.length < 1));
+ assertEq(ch <= "${str}", code < ${code} || (code == ${code} && ch.length <= 1));
+ assertEq(ch > "${str}", code > ${code} || (code == ${code} && ch.length > 1));
+ assertEq(ch >= "${str}", code > ${code} || (code == ${code} && ch.length >= 1));
+ `);
+}
+
+function testCompare(strings, comparator) {
+ // Don't Ion compile to ensure |comparator| won't be inlined.
+ with ({}) ;
+
+ for (let i = 0; i < 1000; ++i) {
+ let str = strings[i % strings.length];
+
+ comparator("", -1);
+
+ for (let j = 0; j < str.length; ++j) {
+ let ch = str.charAt(j);
+ let code = str.charCodeAt(j);
+
+ comparator(ch, code);
+ comparator(ch + "A", code);
+ }
+ }
+}
+
+// Compare against the Latin-1 character U+0062 ('b').
+testCompare([
+ "",
+ "a", "b", "c",
+ "a-", "b-", "c-",
+ "a\u{100}", "b\u{100}", "c\u{100}",
+], makeComparator(0x62));
+
+// Compare against the maximum Latin-1 character U+00FF.
+testCompare([
+ "",
+ "a", "b", "c",
+ "a-", "b-", "c-",
+ "\u{fe}", "\u{ff}", "\u{100}",
+ "\u{fe}-", "\u{ff}-", "\u{100}-",
+], makeComparator(0xff));
+
+// Compare against the Two-byte character U+0101.
+testCompare([
+ "",
+ "a", "b", "c",
+ "a-", "b-", "c-",
+ "\u{100}", "\u{101}", "\u{102}",
+ "\u{100}-", "\u{101}-", "\u{102}-",
+], makeComparator(0x101));
diff --git a/js/src/jit-test/tests/warp/string-endswith-constant-string.js b/js/src/jit-test/tests/warp/string-endswith-constant-string.js
new file mode 100644
index 0000000000..e815f8e376
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-endswith-constant-string.js
@@ -0,0 +1,81 @@
+// Test case to cover String.prototype.endsWith with a constant search string.
+//
+// String.prototype.endsWith with a short (≤32 characters) constant string is
+// optimised during lowering.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+for (let i = 1; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ // Same string as the input.
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Same length as the input, but a different string.
+ String.fromCodePoint(...codePoints.slice(1, i + 1)),
+
+ // Shorter string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i - 1)),
+
+ // Longer string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i + 1)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+
+ for (let codePoints of [ascii, latin1, twoByte]) {
+ let str = String.fromCodePoint(...codePoints.slice(0, i));
+
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.endsWith("${str}");
+ });
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.endsWith("${str}");
+ if (actual !== expected[idx]) throw new Error();
+ }
+ `);
+ fn(strings);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-indexof-constant-string-length-one.js b/js/src/jit-test/tests/warp/string-indexof-constant-string-length-one.js
new file mode 100644
index 0000000000..17ae97c0ad
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-indexof-constant-string-length-one.js
@@ -0,0 +1,74 @@
+// Test case to cover String.prototype.{indexOf,lastIndexOf,includes} with a constant string of length one.
+
+const strings = [
+ // Empty string.
+ "",
+
+ // Latin-1 string.
+ "abcdefgh",
+
+ // Two-byte string.
+ "\u{101}\u{102}\u{103}\u{104}\u{105}\u{106}\u{107}\u{108}",
+].flatMap(x => [
+ x,
+
+ // Add leading characters.
+ "!".repeat(10) + x,
+
+ // Add trailing characters.
+ x + "!".repeat(10),
+]).flatMap(x => [
+ x,
+
+ // To cover the case when the string is two-byte, but has only Latin-1 contents.
+ newString(x, {twoByte: true}),
+]);
+
+const searchStrings = [
+ // Latin-1 search strings:
+ // - at the start of the input string
+ "a",
+ // - in the middle of the input string
+ "d",
+ // - at the end of the input string
+ "h",
+ // - not in the input string
+ "z",
+
+ // Two-byte search strings:
+ // - at the start of the input string
+ "\u{101}",
+ // - in the middle of the input string
+ "\u{104}",
+ // - at the end of the input string
+ "\u{108}",
+ // - not in the input string
+ "\u{1000}",
+];
+
+const stringFunctions = [
+ "indexOf",
+ "lastIndexOf",
+ "includes",
+];
+
+for (let stringFunction of stringFunctions) {
+ for (let searchString of searchStrings) {
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.${stringFunction}("${searchString}");
+ });
+
+ for (let i = 0; i < 500; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.${stringFunction}("${searchString}");
+ assertEq(actual, expected[idx]);
+ }
+ `);
+ fn(strings);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-indexof-constant-string-length-two.js b/js/src/jit-test/tests/warp/string-indexof-constant-string-length-two.js
new file mode 100644
index 0000000000..7659aee53e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-indexof-constant-string-length-two.js
@@ -0,0 +1,74 @@
+// Test case to cover String.prototype.{indexOf,lastIndexOf,includes} with a constant string of length two.
+
+const strings = [
+ // Empty string.
+ "",
+
+ // Latin-1 string.
+ "abcdefgh",
+
+ // Two-byte string.
+ "\u{101}\u{102}\u{103}\u{104}\u{105}\u{106}\u{107}\u{108}",
+].flatMap(x => [
+ x,
+
+ // Add leading characters.
+ "!".repeat(10) + x,
+
+ // Add trailing characters.
+ x + "!".repeat(10),
+]).flatMap(x => [
+ x,
+
+ // To cover the case when the string is two-byte, but has only Latin-1 contents.
+ newString(x, {twoByte: true}),
+]);
+
+const searchStrings = [
+ // Latin-1 search strings:
+ // - at the start of the input string
+ "ab",
+ // - in the middle of the input string
+ "de",
+ // - at the end of the input string
+ "gh",
+ // - not in the input string
+ "zz",
+
+ // Two-byte search strings:
+ // - at the start of the input string
+ "\u{101}\u{102}",
+ // - in the middle of the input string
+ "\u{104}\u{105}",
+ // - at the end of the input string
+ "\u{107}\u{108}",
+ // - not in the input string
+ "\u{1000}\u{1001}",
+];
+
+const stringFunctions = [
+ "indexOf",
+ "lastIndexOf",
+ "includes",
+];
+
+for (let stringFunction of stringFunctions) {
+ for (let searchString of searchStrings) {
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.${stringFunction}("${searchString}");
+ });
+
+ for (let i = 0; i < 500; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.${stringFunction}("${searchString}");
+ assertEq(actual, expected[idx]);
+ }
+ `);
+ fn(strings);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-indexof-constant-string.js b/js/src/jit-test/tests/warp/string-indexof-constant-string.js
new file mode 100644
index 0000000000..1d737ec438
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-indexof-constant-string.js
@@ -0,0 +1,98 @@
+// Test case to cover String.prototype.indexOf with a constant search string.
+//
+// String.prototype.indexOf with a short (≤32 characters) constant string is
+// optimised during lowering.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+for (let i = 1; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ // Same string as the input.
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Same length as the input, but a different string.
+ String.fromCodePoint(...codePoints.slice(1, i + 1)),
+
+ // Shorter string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i - 1)),
+
+ // Longer string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i + 1)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+
+ for (let codePoints of [ascii, latin1, twoByte]) {
+ let str = String.fromCodePoint(...codePoints.slice(0, i));
+
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.indexOf("${str}") === 0;
+ });
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.indexOf("${str}") === 0;
+ if (actual !== expected[idx]) throw new Error();
+ }
+ `);
+ fn(strings);
+
+ let fnNot = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.indexOf("${str}") !== 0;
+ });
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.indexOf("${str}") !== 0;
+ if (actual !== expected[idx]) throw new Error();
+ }
+ `);
+ fnNot(strings);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-indexof-is-startswith.js b/js/src/jit-test/tests/warp/string-indexof-is-startswith.js
new file mode 100644
index 0000000000..1d9f31e38c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-indexof-is-startswith.js
@@ -0,0 +1,29 @@
+// |str.indexOf(searchStr) == 0| is compiled as |str.startsWith(searchStr)|.
+// |str.indexOf(searchStr) != 0| is compiled as |!str.startsWith(searchStr)|.
+
+const strings = [
+ "",
+ "a", "b",
+ "ab", "ba", "ac", "ca",
+ "aba", "abb", "abc", "aca",
+];
+
+function StringIndexOf(str, searchStr) {
+ // Prevent Warp compilation when computing the expected result.
+ with ({});
+ return str.indexOf(searchStr);
+}
+
+for (let str of strings) {
+ for (let searchStr of strings) {
+ let startsWith = str.indexOf(searchStr) === 0;
+
+ assertEq(startsWith, str.startsWith(searchStr));
+ assertEq(startsWith, StringIndexOf(str, searchStr) === 0);
+
+ let notStartsWith = str.indexOf(searchStr) !== 0;
+
+ assertEq(notStartsWith, !str.startsWith(searchStr));
+ assertEq(notStartsWith, StringIndexOf(str, searchStr) !== 0);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-startswith-constant-string.js b/js/src/jit-test/tests/warp/string-startswith-constant-string.js
new file mode 100644
index 0000000000..48a3f6dc73
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-startswith-constant-string.js
@@ -0,0 +1,81 @@
+// Test case to cover String.prototype.startsWith with a constant search string.
+//
+// String.prototype.startsWith with a short (≤32 characters) constant string is
+// optimised during lowering.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+for (let i = 1; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ // Same string as the input.
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Same length as the input, but a different string.
+ String.fromCodePoint(...codePoints.slice(1, i + 1)),
+
+ // Shorter string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i - 1)),
+
+ // Longer string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i + 1)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+
+ for (let codePoints of [ascii, latin1, twoByte]) {
+ let str = String.fromCodePoint(...codePoints.slice(0, i));
+
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.startsWith("${str}");
+ });
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.startsWith("${str}");
+ if (actual !== expected[idx]) throw new Error();
+ }
+ `);
+ fn(strings);
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-substring-is-charat.js b/js/src/jit-test/tests/warp/string-substring-is-charat.js
new file mode 100644
index 0000000000..0675aa2029
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-substring-is-charat.js
@@ -0,0 +1,13 @@
+// |str.substring(0, 1)| is compiled as |str.charAt(0)|.
+
+const strings = [
+ "",
+ "a", "b",
+ "ab", "ba",
+];
+
+for (let i = 0; i < 1000; ++i) {
+ let str = strings[i % strings.length];
+
+ assertEq(str.substring(0, 1), str.charAt(0));
+}
diff --git a/js/src/jit-test/tests/warp/string-substring-startswith-constant-string.js b/js/src/jit-test/tests/warp/string-substring-startswith-constant-string.js
new file mode 100644
index 0000000000..f98627cfe9
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-substring-startswith-constant-string.js
@@ -0,0 +1,90 @@
+// Test case to cover String.prototype.substring as startsWith with a constant search string.
+//
+// String.prototype.substring comparison with a short (≤32 characters) constant string is
+// optimised during lowering.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+const operators = [
+ "==", "===", "!=", "!==",
+];
+
+for (let i = 1; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ // Same string as the input.
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Same length as the input, but a different string.
+ String.fromCodePoint(...codePoints.slice(1, i + 1)),
+
+ // Shorter string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i - 1)),
+
+ // Longer string than the input.
+ String.fromCodePoint(...codePoints.slice(0, i + 1)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+
+ for (let codePoints of [ascii, latin1, twoByte]) {
+ let str = String.fromCodePoint(...codePoints.slice(0, i));
+
+ for (let op of operators) {
+ let fn = Function("strings", `
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.substring(0, ${str.length}) ${op} "${str}";
+ });
+
+ for (let i = 0; i < 250; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let lhs = str.substring(0, ${str.length}) ${op} "${str}";
+ if (lhs !== expected[idx]) throw new Error();
+
+ let rhs = "${str}" ${op} str.substring(0, ${str.length});
+ if (rhs !== expected[idx]) throw new Error();
+ }
+ `);
+ fn(strings);
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-substring-static-strings.js b/js/src/jit-test/tests/warp/string-substring-static-strings.js
new file mode 100644
index 0000000000..1ea8e36c31
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-substring-static-strings.js
@@ -0,0 +1,20 @@
+// `str.substring(...)` can return static strings.
+
+const strings = [
+ "abcdef",
+ "ABCDEF",
+];
+
+for (let i = 0; i < 500; ++i) {
+ let str = strings[i & 1];
+
+ for (let j = 0; j < 2; ++j) {
+ // One element static string.
+ let r = str.substring(j, j + 1);
+ assertEq(r, str.charAt(j));
+
+ // Two elements static string.
+ let s = str.substring(j, j + 2);
+ assertEq(s, str.charAt(j) + str.charAt(j + 1));
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-tolowercase-latin1.js b/js/src/jit-test/tests/warp/string-tolowercase-latin1.js
new file mode 100644
index 0000000000..2d94850445
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-tolowercase-latin1.js
@@ -0,0 +1,85 @@
+// Test inline lower case conversion of ASCII / Latin-1 strings.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii_upper = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x30, 0x39], // 0..9
+)];
+
+const ascii_lower = [...characters(
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1_upper = [...characters(
+ [0xC0, 0xDE], // À..Þ
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1_lower = [...characters(
+ [0xDF, 0xFF], // ß..ÿ
+)];
+
+for (let upper of [ascii_upper, latin1_upper]) {
+ let s = String.fromCodePoint(...upper);
+ assertEq(isLatin1(s), true);
+ assertEq(s, s.toUpperCase());
+}
+
+for (let lower of [ascii_lower, latin1_lower]) {
+ let s = String.fromCodePoint(...lower);
+ assertEq(isLatin1(s), true);
+ assertEq(s, s.toLowerCase());
+}
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+for (let i = 0; i <= 32; ++i) {
+ let strings = [ascii_upper, ascii_lower, latin1_upper, latin1_lower].flatMap(codePoints => [
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Trailing ASCII upper case character.
+ String.fromCodePoint(...codePoints.slice(0, i)) + "A",
+
+ // Trailing ASCII lower case character.
+ String.fromCodePoint(...codePoints.slice(0, i)) + "a",
+
+ // Trailing Latin-1 upper case character.
+ String.fromCodePoint(...codePoints.slice(0, i)) + "À",
+
+ // Trailing Latin-1 lower case character.
+ String.fromCodePoint(...codePoints.slice(0, i)) + "ß",
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ ]);
+
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.toLowerCase();
+ });
+
+ for (let i = 0; i < 1000; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.toLowerCase();
+ if (actual !== expected[idx]) throw new Error();
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-totitlecase.js b/js/src/jit-test/tests/warp/string-totitlecase.js
new file mode 100644
index 0000000000..0237a263b3
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-totitlecase.js
@@ -0,0 +1,85 @@
+// Test inline title case conversion.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+ [0x30, 0x39], // 0..9
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+String.prototype.toTitleCase = function() {
+ "use strict";
+
+ var s = String(this);
+
+ if (s.length === 0) {
+ return s;
+ }
+ return s[0].toUpperCase() + s.substring(1);
+};
+
+function toRope(s) {
+ try {
+ return newRope(s[0], s.substring(1));
+ } catch {}
+ // newRope can fail when |s| fits into an inline string. In that case simply
+ // return the input.
+ return s;
+}
+
+for (let i = 0; i <= 32; ++i) {
+ let strings = [ascii, latin1, twoByte].flatMap(codePoints => [
+ String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Leading ASCII upper case character.
+ "A" + String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Leading ASCII lower case character.
+ "a" + String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Leading Latin-1 upper case character.
+ "À" + String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Leading Latin-1 lower case character.
+ "à" + String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Leading Two-Byte upper case character.
+ "Ā" + String.fromCodePoint(...codePoints.slice(0, i)),
+
+ // Leading Two-Byte lower case character.
+ "ā" + String.fromCodePoint(...codePoints.slice(0, i)),
+ ]).flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ ]);
+
+ const expected = strings.map(x => {
+ // Prevent Warp compilation when computing the expected results.
+ with ({}) ;
+ return x.toTitleCase();
+ });
+
+ for (let i = 0; i < 1000; ++i) {
+ let idx = i % strings.length;
+ let str = strings[idx];
+
+ let actual = str.toTitleCase();
+ if (actual !== expected[idx]) throw new Error();
+ }
+}
diff --git a/js/src/jit-test/tests/warp/string-trim.js b/js/src/jit-test/tests/warp/string-trim.js
new file mode 100644
index 0000000000..0ca05be8ac
--- /dev/null
+++ b/js/src/jit-test/tests/warp/string-trim.js
@@ -0,0 +1,75 @@
+const whitespace = [
+ // Ascii whitespace
+ " ",
+ "\t",
+
+ // Latin-1 whitespace
+ "\u{a0}",
+
+ // Two-byte whitespace
+ "\u{1680}",
+];
+
+const strings = [
+ // Empty string
+ "",
+
+ // Ascii strings
+ "a",
+ "abc",
+
+ // Latin-1 strings
+ "á",
+ "áèô",
+
+ // Two-byte strings
+ "\u{100}",
+ "\u{100}\u{101}\u{102}",
+].flatMap(x => [
+ x,
+
+ // Leading whitespace
+ ...whitespace.flatMap(w => [w + x, w + w + x]),
+
+ // Trailing whitespace
+ ...whitespace.flatMap(w => [x + w, x + w + w]),
+
+ // Leading and trailing whitespace
+ ...whitespace.flatMap(w => [w + x + w, w + w + x + w + w]),
+
+ // Interspersed whitespace
+ ...whitespace.flatMap(w => [x + w + x, x + w + w + x]),
+]);
+
+function trim(strings) {
+ // Compute expected values using RegExp.
+ let expected = strings.map(x => x.replace(/(^\s+)|(\s+$)/g, ""));
+
+ for (let i = 0; i < 1000; ++i) {
+ let index = i % strings.length;
+ assertEq(strings[index].trim(), expected[index]);
+ }
+}
+for (let i = 0; i < 2; ++i) trim(strings);
+
+function trimStart(strings) {
+ // Compute expected values using RegExp.
+ let expected = strings.map(x => x.replace(/(^\s+)/g, ""));
+
+ for (let i = 0; i < 1000; ++i) {
+ let index = i % strings.length;
+ assertEq(strings[index].trimStart(), expected[index]);
+ }
+}
+for (let i = 0; i < 2; ++i) trimStart(strings);
+
+function trimEnd(strings) {
+ // Compute expected values using RegExp.
+ let expected = strings.map(x => x.replace(/(\s+$)/g, ""));
+
+ for (let i = 0; i < 1000; ++i) {
+ let index = i % strings.length;
+ assertEq(strings[index].trimEnd(), expected[index]);
+ }
+}
+for (let i = 0; i < 2; ++i) trimEnd(strings);
diff --git a/js/src/jit-test/tests/warp/stub-folding-add-case.js b/js/src/jit-test/tests/warp/stub-folding-add-case.js
new file mode 100644
index 0000000000..a31b0f2e7e
--- /dev/null
+++ b/js/src/jit-test/tests/warp/stub-folding-add-case.js
@@ -0,0 +1,35 @@
+// |jit-test| --fast-warmup
+
+var sum = 0;
+function foo(o) {
+ sum += o.a;
+}
+
+with({}) {}
+
+// Fold a stub with two cases.
+for (var i = 0; i < 11; i++) {
+ foo({a:1});
+ foo({a:1,b:2});
+}
+
+// Add a third case.
+foo({a:1,b:2,c:3});
+
+// Warp-compile.
+for (var i = 0; i < 16; i++) {
+ foo({a:1});
+}
+
+// Add a fourth case.
+foo({a:1,b:2,c:3,d:4});
+
+// Run for a while in Warp.
+for (var i = 0; i < 20; i++) {
+ foo({a:1});
+ foo({a:1,b:2});
+ foo({a:1,b:2,c:3});
+ foo({a:1,b:2,c:3,d:4});
+}
+
+assertEq(sum, 120);
diff --git a/js/src/jit-test/tests/warp/stub-folding-cross-compartment.js b/js/src/jit-test/tests/warp/stub-folding-cross-compartment.js
new file mode 100644
index 0000000000..735504cf89
--- /dev/null
+++ b/js/src/jit-test/tests/warp/stub-folding-cross-compartment.js
@@ -0,0 +1,15 @@
+var src = `
+var p = {y: 1};
+var arr = [];
+for (var i = 0; i < 10; i++) {
+ var o = Object.create(p);
+ o["x" + i] = 2;
+ arr.push(o);
+}
+arr
+`;
+
+var wrapper = evaluate(src, {global: newGlobal({sameZoneAs: this})});
+for (var i = 0; i < 50; i++) {
+ assertEq(wrapper[i % 10].y, 1);
+}
diff --git a/js/src/jit-test/tests/warp/stub-folding-transition.js b/js/src/jit-test/tests/warp/stub-folding-transition.js
new file mode 100644
index 0000000000..865e6dc189
--- /dev/null
+++ b/js/src/jit-test/tests/warp/stub-folding-transition.js
@@ -0,0 +1,24 @@
+var sum = 0;
+function foo(o) {
+ sum += o.x;
+}
+
+with({}) {}
+
+// Trigger stub folding in MaybeTransition
+for (var i = 0; i < 200; i++) {
+ foo({x:1, a:1});
+ foo({x:1, b:1});
+ foo({x:1, c:1});
+ foo({x:1, d:1});
+ foo({x:1, e:1});
+ foo({x:1, f:1});
+ foo({x:1, g:1});
+ foo({x:1, h:1});
+ foo({x:1, i:1});
+ foo({x:1, j:1});
+ foo({x:1, k:1});
+ foo({x:1, l:1});
+}
+
+assertEq(sum, 2400);
diff --git a/js/src/jit-test/tests/warp/stub-folding.js b/js/src/jit-test/tests/warp/stub-folding.js
new file mode 100644
index 0000000000..0b7d7fdda4
--- /dev/null
+++ b/js/src/jit-test/tests/warp/stub-folding.js
@@ -0,0 +1,35 @@
+// |jit-test| --fast-warmup
+
+with({}) {}
+
+class A {
+ foo() { return 3; }
+ get z() { return 5; }
+}
+
+class B1 extends A {
+ constructor(y) {
+ super();
+ this.y = y;
+ }
+}
+
+class B2 extends A {
+ constructor(x,y) {
+ super();
+ this.y = y;
+ this.x = x;
+ }
+}
+
+var sum = 0;
+function foo(o) {
+ sum += o.foo() + o.y + o.z;
+}
+
+for (var i = 0; i < 50; i++) {
+ foo(new B1(i));
+ foo(new B2(i,i));
+}
+
+assertEq(sum, 3250);
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/throw-exception-stack-location.js b/js/src/jit-test/tests/warp/throw-exception-stack-location.js
new file mode 100644
index 0000000000..bcb7329b05
--- /dev/null
+++ b/js/src/jit-test/tests/warp/throw-exception-stack-location.js
@@ -0,0 +1,41 @@
+function throwValue(value) {
+ throw value;
+}
+
+// Test try-finally keep the exception stack.
+function testFinally() {
+ function f() {
+ try {
+ throwValue("exception-value");
+ } finally {
+ for (let i = 0; i < 100; ++i) {
+ // OSR
+ }
+ }
+ }
+
+ let info = getExceptionInfo(f);
+ assertEq(info.exception, "exception-value");
+ assertEq(info.stack.includes("throwValue"), true);
+}
+testFinally();
+
+// Test try-catch-finally keep the exception stack.
+function testCatchFinally() {
+ function f() {
+ try {
+ throw null;
+ } catch {
+ throwValue("exception-value");
+ } finally {
+ for (let i = 0; i < 100; ++i) {
+ // OSR
+ }
+ }
+ }
+
+ let info = getExceptionInfo(f);
+ assertEq(info.exception, "exception-value");
+ assertEq(info.stack.includes("throwValue"), true);
+}
+testCatchFinally();
diff --git a/js/src/jit-test/tests/warp/trial-inline-gc-1.js b/js/src/jit-test/tests/warp/trial-inline-gc-1.js
new file mode 100644
index 0000000000..6129221bb8
--- /dev/null
+++ b/js/src/jit-test/tests/warp/trial-inline-gc-1.js
@@ -0,0 +1,14 @@
+// |jit-test| --fast-warmup
+function g(x) {
+ return x + 1;
+}
+function f() {
+ for (var i = 0; i < 150; i++) {
+ assertEq(g("foo"), "foo1");
+ assertEq(g(1), 2);
+ if (i === 100) {
+ gc(this, "shrinking");
+ }
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/warp/trial-inline-gc-2.js b/js/src/jit-test/tests/warp/trial-inline-gc-2.js
new file mode 100644
index 0000000000..058e2ff645
--- /dev/null
+++ b/js/src/jit-test/tests/warp/trial-inline-gc-2.js
@@ -0,0 +1,18 @@
+// |jit-test| --fast-warmup
+function h(i) {
+ with(this) {} // Don't inline.
+ if (i === 100) {
+ gc(this, "shrinking");
+ }
+}
+function g(i, x) {
+ h(i);
+ return x + 1;
+}
+function f() {
+ for (var i = 0; i < 200; i++) {
+ g(i, "foo");
+ g(i, 1);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/warp/trial-inline-gc-3.js b/js/src/jit-test/tests/warp/trial-inline-gc-3.js
new file mode 100644
index 0000000000..4e64a22167
--- /dev/null
+++ b/js/src/jit-test/tests/warp/trial-inline-gc-3.js
@@ -0,0 +1,23 @@
+// |jit-test| --fast-warmup
+function h(i) {
+ if (i === 150) {
+ with(this) {} // Don't inline.
+ // Trigger an invalidation of f's IonScript (with g inlined into it) on
+ // the stack. Before we bail out, replace g and trial-inline the new
+ // function. The bailout must not get confused by this.
+ gc(this, "shrinking");
+ g = (i, x) => x + 20;
+ f();
+ }
+}
+function g(i, x) {
+ h(i);
+ return x + 1;
+}
+function f() {
+ for (var i = 0; i < 300; i++) {
+ g(i, "foo");
+ g(i, 1);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/warp/try-catch-unwind.js b/js/src/jit-test/tests/warp/try-catch-unwind.js
new file mode 100644
index 0000000000..a167e03f5d
--- /dev/null
+++ b/js/src/jit-test/tests/warp/try-catch-unwind.js
@@ -0,0 +1,29 @@
+let throwing = false;
+
+function bar() {
+ with ({}) {}
+ if (throwing) throw 3;
+}
+
+function foo() {
+ let y = 3;
+ try {
+ let x = 3;
+ () => { return x + y; }
+ bar();
+ } catch (e) {
+ assertEq(y, 3);
+ throw e;
+ }
+}
+
+with ({}) {}
+
+for (var i = 0; i < 1000; i++) {
+ foo()
+}
+
+throwing = true;
+try {
+ foo();
+} catch {}
diff --git a/js/src/jit-test/tests/warp/try-finally-1.js b/js/src/jit-test/tests/warp/try-finally-1.js
new file mode 100644
index 0000000000..206286d598
--- /dev/null
+++ b/js/src/jit-test/tests/warp/try-finally-1.js
@@ -0,0 +1,36 @@
+let target = 3;
+function throwOn(x) {
+ with ({}) {}
+ if (x == target) {
+ throw 3;
+ }
+}
+
+// Test try-finally without catch
+function foo() {
+ var x = 0;
+ try {
+ throwOn(0);
+ x = 1;
+ throwOn(1);
+ x = 2;
+ throwOn(2);
+ x = 3;
+ } finally {
+ assertEq(x, target);
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 1000; i++) {
+ try {
+ foo();
+ } catch {}
+}
+
+for (var i = 0; i < 4; i++) {
+ target = i;
+ try {
+ foo();
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/warp/try-finally-2.js b/js/src/jit-test/tests/warp/try-finally-2.js
new file mode 100644
index 0000000000..4187a37e0c
--- /dev/null
+++ b/js/src/jit-test/tests/warp/try-finally-2.js
@@ -0,0 +1,42 @@
+let catchCount = 0;
+
+let target = 3;
+function throwOn(x) {
+ with ({}) {}
+ if (x == target) {
+ throw 3;
+ }
+}
+
+// Test try-catch-finally
+function foo() {
+ var x = 0;
+ try {
+ throwOn(0);
+ x = 1;
+ throwOn(1);
+ x = 2;
+ throwOn(2);
+ x = 3;
+ } catch {
+ catchCount++;
+ } finally {
+ assertEq(x, target);
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 1000; i++) {
+ try {
+ foo();
+ } catch {}
+}
+
+for (var i = 0; i < 4; i++) {
+ target = i;
+ try {
+ foo();
+ } catch {}
+}
+
+assertEq(catchCount, 3);
diff --git a/js/src/jit-test/tests/warp/try-finally-3.js b/js/src/jit-test/tests/warp/try-finally-3.js
new file mode 100644
index 0000000000..98811486db
--- /dev/null
+++ b/js/src/jit-test/tests/warp/try-finally-3.js
@@ -0,0 +1,35 @@
+var target = undefined;
+function throwOn(x) {
+ with ({}) {}
+ if (x === target) {
+ throw x;
+ }
+}
+
+// Test nested try-finally
+function foo() {
+ let result = 0;
+ try {
+ throwOn(1);
+ result += 1;
+ try {
+ throwOn(2);
+ result += 2;
+ } finally {
+ result += 3;
+ }
+ } finally {
+ return result;
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ assertEq(foo(), 6);
+}
+
+target = 1;
+assertEq(foo(), 0);
+
+target = 2;
+assertEq(foo(), 4);
diff --git a/js/src/jit-test/tests/warp/try-finally-unwind.js b/js/src/jit-test/tests/warp/try-finally-unwind.js
new file mode 100644
index 0000000000..05860fbaa5
--- /dev/null
+++ b/js/src/jit-test/tests/warp/try-finally-unwind.js
@@ -0,0 +1,28 @@
+let throwing = false;
+
+function bar() {
+ with ({}) {}
+ if (throwing) throw 3;
+}
+
+function foo() {
+ let y = 3;
+ try {
+ let x = 3;
+ () => { return x + y; }
+ bar();
+ } finally {
+ assertEq(y, 3);
+ }
+}
+
+with ({}) {}
+
+for (var i = 0; i < 1000; i++) {
+ foo()
+}
+
+throwing = true;
+try {
+ foo();
+} catch {}
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);
+}
diff --git a/js/src/jit-test/tests/warp/typeof-is.js b/js/src/jit-test/tests/warp/typeof-is.js
new file mode 100644
index 0000000000..df41185769
--- /dev/null
+++ b/js/src/jit-test/tests/warp/typeof-is.js
@@ -0,0 +1,126 @@
+// Test case |typeof| folding in simple comparison contexts.
+
+// Create functions to test if |typeof x| is equal to a constant string.
+// - Covers all possible |typeof| results, plus the invalid string "bad".
+// - Covers all four possible equality comparison operators.
+function createFunctions() {
+ return [
+ "undefined",
+ "object",
+ "function",
+ "string",
+ "number",
+ "boolean",
+ "symbol",
+ "bigint",
+ "bad",
+ ].flatMap(type => [
+ "==",
+ "===",
+ "!=",
+ "!=="
+ ].map(op => ({
+ type,
+ equal: op[0] === "=",
+ fn: Function("thing", `return typeof thing ${op} "${type}"`)
+ })));
+}
+
+let functions = createFunctions();
+
+function test() {
+ const ccwGlobal = newGlobal({newCompartment: true});
+ const xs = [
+ // "undefined"
+ // Plain undefined and object emulating undefined, including various proxy wrapper cases.
+ undefined,
+ createIsHTMLDDA(),
+ wrapWithProto(createIsHTMLDDA(), null),
+ ccwGlobal.eval("createIsHTMLDDA()"),
+
+ // "object"
+ // Plain object and various proxy wrapper cases.
+ {},
+ this,
+ new Proxy({}, {}),
+ wrapWithProto({}, null),
+ transplantableObject({proxy: true}).object,
+ ccwGlobal.Object(),
+
+ // "function"
+ // Plain function and various proxy wrapper cases.
+ function(){},
+ new Proxy(function(){}, {}),
+ new Proxy(createIsHTMLDDA(), {}),
+ wrapWithProto(function(){}, null),
+ ccwGlobal.Function(),
+
+ // "string"
+ "",
+
+ // "number"
+ // Int32 and Double numbers.
+ 0,
+ 1.23,
+
+ // "boolean"
+ true,
+
+ // "symbol"
+ Symbol(),
+
+ // "bigint"
+ 0n,
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i % xs.length];
+ for (let {type, equal, fn} of functions) {
+ assertEq(fn(x), (typeof x === type) === equal);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) test();
+
+// Fresh set of functions to gather new type info.
+let functionsObject = createFunctions();
+
+// Additional test when the input is always an object.
+function testObject() {
+ const ccwGlobal = newGlobal({newCompartment: true});
+
+ // All elements of this array are objects to cover the case when |MTypeOf| has
+ // a |MIRType::Object| input.
+ const xs = [
+ // "undefined"
+ // Object emulating undefined, including various proxy wrapper cases.
+ createIsHTMLDDA(),
+ wrapWithProto(createIsHTMLDDA(), null),
+ ccwGlobal.eval("createIsHTMLDDA()"),
+
+ // "object"
+ // Plain object and various proxy wrapper cases.
+ {},
+ this,
+ new Proxy({}, {}),
+ wrapWithProto({}, null),
+ transplantableObject({proxy: true}).object,
+ ccwGlobal.Object(),
+
+ // "function"
+ // Plain function and various proxy wrapper cases.
+ function(){},
+ new Proxy(function(){}, {}),
+ new Proxy(createIsHTMLDDA(), {}),
+ wrapWithProto(function(){}, null),
+ ccwGlobal.Function(),
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i % xs.length];
+ for (let {type, equal, fn} of functionsObject) {
+ assertEq(fn(x), (typeof x === type) === equal);
+ }
+ }
+}
+for (let i = 0; i < 2; ++i) testObject();
diff --git a/js/src/jit-test/tests/warp/typeof-switch.js b/js/src/jit-test/tests/warp/typeof-switch.js
new file mode 100644
index 0000000000..4dc33c3686
--- /dev/null
+++ b/js/src/jit-test/tests/warp/typeof-switch.js
@@ -0,0 +1,78 @@
+// Test case |typeof| folding in switch-statement contexts.
+
+function TypeOf(thing) {
+ switch (typeof thing) {
+ case "undefined":
+ return "undefined";
+ case "object":
+ return "object";
+ case "function":
+ return "function";
+ case "string":
+ return "string";
+ case "number":
+ return "number";
+ case "boolean":
+ return "boolean";
+ case "symbol":
+ return "symbol";
+ case "bigint":
+ return "bigint";
+ case "bad":
+ return "bad";
+ }
+ return "bad2";
+}
+
+function test() {
+ const ccwGlobal = newGlobal({newCompartment: true});
+ const xs = [
+ // "undefined"
+ // Plain undefined and objects emulating undefined, including various
+ // proxy wrapper cases.
+ undefined,
+ createIsHTMLDDA(),
+ wrapWithProto(createIsHTMLDDA(), null),
+ ccwGlobal.eval("createIsHTMLDDA()"),
+
+ // "object"
+ // Plain objects and various proxy wrapper cases.
+ {},
+ this,
+ new Proxy({}, {}),
+ wrapWithProto({}, null),
+ transplantableObject({proxy: true}).object,
+ ccwGlobal.Object(),
+
+ // "function"
+ // Plain functions and various proxy wrapper cases.
+ function(){},
+ new Proxy(function(){}, {}),
+ new Proxy(createIsHTMLDDA(), {}),
+ wrapWithProto(function(){}, null),
+ ccwGlobal.Function(),
+
+ // "string"
+ "",
+
+ // "number"
+ // Int32 and Double numbers.
+ 0,
+ 1.23,
+
+ // "boolean"
+ true,
+
+ // "symbol"
+ Symbol(),
+
+ // "bigint"
+ 0n,
+ ];
+
+ for (let i = 0; i < 500; ++i) {
+ let x = xs[i % xs.length];
+ assertEq(TypeOf(x), typeof x);
+ }
+}
+for (let i = 0; i < 2; ++i) test();