summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/cacheir
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/jit-test/tests/cacheir/1877684.js6
-rw-r--r--js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js160
-rw-r--r--js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js160
-rw-r--r--js/src/jit-test/tests/cacheir/add-dense-element.js124
-rw-r--r--js/src/jit-test/tests/cacheir/add-function-prototype.js55
-rw-r--r--js/src/jit-test/tests/cacheir/alloc-dense-elements.js9
-rw-r--r--js/src/jit-test/tests/cacheir/apply-minmax-1.js23
-rw-r--r--js/src/jit-test/tests/cacheir/apply-minmax-2.js23
-rw-r--r--js/src/jit-test/tests/cacheir/apply-minmax-3.js25
-rw-r--r--js/src/jit-test/tests/cacheir/apply-minmax-4.js15
-rw-r--r--js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js171
-rw-r--r--js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js184
-rw-r--r--js/src/jit-test/tests/cacheir/array-slice.js39
-rw-r--r--js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js123
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-binary.js190
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-compare-double.js209
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-compare-int32.js258
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js82
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-compare-number.js205
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-compare-string.js189
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-compare.js163
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-tobool.js72
-rw-r--r--js/src/jit-test/tests/cacheir/bigint-unary.js135
-rw-r--r--js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js70
-rw-r--r--js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js75
-rw-r--r--js/src/jit-test/tests/cacheir/binaryarith.js322
-rw-r--r--js/src/jit-test/tests/cacheir/bind-function-specialized.js72
-rw-r--r--js/src/jit-test/tests/cacheir/bindname-lexical-errors.js26
-rw-r--r--js/src/jit-test/tests/cacheir/boolean-call.js67
-rw-r--r--js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js149
-rw-r--r--js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js46
-rw-r--r--js/src/jit-test/tests/cacheir/bound-construct-hook.js18
-rw-r--r--js/src/jit-test/tests/cacheir/bound-construct-scripted.js72
-rw-r--r--js/src/jit-test/tests/cacheir/bug1345707.js7
-rw-r--r--js/src/jit-test/tests/cacheir/bug1357024.js12
-rw-r--r--js/src/jit-test/tests/cacheir/bug1397026.js43
-rw-r--r--js/src/jit-test/tests/cacheir/bug1414849.js12
-rw-r--r--js/src/jit-test/tests/cacheir/bug1420910.js35
-rw-r--r--js/src/jit-test/tests/cacheir/bug1423139.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1438727.1.js22
-rw-r--r--js/src/jit-test/tests/cacheir/bug1438727.2.js6
-rw-r--r--js/src/jit-test/tests/cacheir/bug1438727.3.js16
-rw-r--r--js/src/jit-test/tests/cacheir/bug1438727.4.js4
-rw-r--r--js/src/jit-test/tests/cacheir/bug1438727.js7
-rw-r--r--js/src/jit-test/tests/cacheir/bug1439180.js9
-rw-r--r--js/src/jit-test/tests/cacheir/bug1448136.js23
-rw-r--r--js/src/jit-test/tests/cacheir/bug1451976.js11
-rw-r--r--js/src/jit-test/tests/cacheir/bug1451984.js10
-rw-r--r--js/src/jit-test/tests/cacheir/bug1459754.js9
-rw-r--r--js/src/jit-test/tests/cacheir/bug1462280.js3
-rw-r--r--js/src/jit-test/tests/cacheir/bug1471361.js13
-rw-r--r--js/src/jit-test/tests/cacheir/bug1483183.js8
-rw-r--r--js/src/jit-test/tests/cacheir/bug1488786-2.js27
-rw-r--r--js/src/jit-test/tests/cacheir/bug1488786.js38
-rw-r--r--js/src/jit-test/tests/cacheir/bug1494537-plain.js123
-rw-r--r--js/src/jit-test/tests/cacheir/bug1494537.js125
-rw-r--r--js/src/jit-test/tests/cacheir/bug1500255.js11
-rw-r--r--js/src/jit-test/tests/cacheir/bug1502143.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1502709.js17
-rw-r--r--js/src/jit-test/tests/cacheir/bug1509293.js2
-rw-r--r--js/src/jit-test/tests/cacheir/bug1514682.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1526872.js12
-rw-r--r--js/src/jit-test/tests/cacheir/bug1536228.js40
-rw-r--r--js/src/jit-test/tests/cacheir/bug1612636.js10
-rw-r--r--js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js28
-rw-r--r--js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js23
-rw-r--r--js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js24
-rw-r--r--js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js24
-rw-r--r--js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js23
-rw-r--r--js/src/jit-test/tests/cacheir/bug1654947.js11
-rw-r--r--js/src/jit-test/tests/cacheir/bug1685684.js8
-rw-r--r--js/src/jit-test/tests/cacheir/bug1685925-1.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1685925-2.js7
-rw-r--r--js/src/jit-test/tests/cacheir/bug1713556.js25
-rw-r--r--js/src/jit-test/tests/cacheir/bug1757634.js29
-rw-r--r--js/src/jit-test/tests/cacheir/bug1772824.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1785200.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1788528-1.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1788528-2.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1788528-3.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1788528-4.js15
-rw-r--r--js/src/jit-test/tests/cacheir/bug1804634.js34
-rw-r--r--js/src/jit-test/tests/cacheir/bug1819486.js18
-rw-r--r--js/src/jit-test/tests/cacheir/bug1823212.js13
-rw-r--r--js/src/jit-test/tests/cacheir/bug1834038.js9
-rw-r--r--js/src/jit-test/tests/cacheir/bug1837157.js36
-rw-r--r--js/src/jit-test/tests/cacheir/bug1842617.js30
-rw-r--r--js/src/jit-test/tests/cacheir/bug1851599.js44
-rw-r--r--js/src/jit-test/tests/cacheir/bug1851911.js35
-rw-r--r--js/src/jit-test/tests/cacheir/bug1852893-1.js27
-rw-r--r--js/src/jit-test/tests/cacheir/bug1852893-2.js42
-rw-r--r--js/src/jit-test/tests/cacheir/call-any-native.js59
-rw-r--r--js/src/jit-test/tests/cacheir/call-bound-function-many-args.js63
-rw-r--r--js/src/jit-test/tests/cacheir/call-bound-scripted.js84
-rw-r--r--js/src/jit-test/tests/cacheir/call-native-get-element-super.js44
-rw-r--r--js/src/jit-test/tests/cacheir/ccw-missing.js4
-rw-r--r--js/src/jit-test/tests/cacheir/compare-null-or-undef.js93
-rw-r--r--js/src/jit-test/tests/cacheir/compare.js292
-rw-r--r--js/src/jit-test/tests/cacheir/construct-bound-realm.js10
-rw-r--r--js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js126
-rw-r--r--js/src/jit-test/tests/cacheir/dom-call.js29
-rw-r--r--js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js38
-rw-r--r--js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js46
-rw-r--r--js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js39
-rw-r--r--js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js42
-rw-r--r--js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js50
-rw-r--r--js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js36
-rw-r--r--js/src/jit-test/tests/cacheir/fun-call-apply-weird.js31
-rw-r--r--js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js59
-rw-r--r--js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js20
-rw-r--r--js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js35
-rw-r--r--js/src/jit-test/tests/cacheir/function-length.js48
-rw-r--r--js/src/jit-test/tests/cacheir/function-name.js59
-rw-r--r--js/src/jit-test/tests/cacheir/generic-spreadcall.js14
-rw-r--r--js/src/jit-test/tests/cacheir/getelem-undefined-null.js52
-rw-r--r--js/src/jit-test/tests/cacheir/getpropsuper.js15
-rw-r--r--js/src/jit-test/tests/cacheir/getter-primitive-value.js116
-rw-r--r--js/src/jit-test/tests/cacheir/getter-setter-guards1.js64
-rw-r--r--js/src/jit-test/tests/cacheir/getter-setter-guards2.js130
-rw-r--r--js/src/jit-test/tests/cacheir/global-getter.js36
-rw-r--r--js/src/jit-test/tests/cacheir/global-setter.js39
-rw-r--r--js/src/jit-test/tests/cacheir/has-sparse.js58
-rw-r--r--js/src/jit-test/tests/cacheir/has-undefined-null.js28
-rw-r--r--js/src/jit-test/tests/cacheir/has.js66
-rw-r--r--js/src/jit-test/tests/cacheir/hasown.js46
-rw-r--r--js/src/jit-test/tests/cacheir/iter-megamorphic.js18
-rw-r--r--js/src/jit-test/tests/cacheir/load-typed-element-bigint.js100
-rw-r--r--js/src/jit-test/tests/cacheir/map-get-bigint.js88
-rw-r--r--js/src/jit-test/tests/cacheir/map-get-nongcthing.js116
-rw-r--r--js/src/jit-test/tests/cacheir/map-get-object.js31
-rw-r--r--js/src/jit-test/tests/cacheir/map-get-string.js83
-rw-r--r--js/src/jit-test/tests/cacheir/map-get-symbol.js31
-rw-r--r--js/src/jit-test/tests/cacheir/map-get-value.js31
-rw-r--r--js/src/jit-test/tests/cacheir/map-has-bigint.js84
-rw-r--r--js/src/jit-test/tests/cacheir/map-has-nongcthing.js110
-rw-r--r--js/src/jit-test/tests/cacheir/map-has-object.js30
-rw-r--r--js/src/jit-test/tests/cacheir/map-has-string.js79
-rw-r--r--js/src/jit-test/tests/cacheir/map-has-symbol.js30
-rw-r--r--js/src/jit-test/tests/cacheir/map-has-value.js30
-rw-r--r--js/src/jit-test/tests/cacheir/map-size.js43
-rw-r--r--js/src/jit-test/tests/cacheir/math-min-max.js180
-rw-r--r--js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js63
-rw-r--r--js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js38
-rw-r--r--js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js129
-rw-r--r--js/src/jit-test/tests/cacheir/nukedCCW.js40
-rw-r--r--js/src/jit-test/tests/cacheir/number-parseInt-double.js205
-rw-r--r--js/src/jit-test/tests/cacheir/number-parseInt-int32.js93
-rw-r--r--js/src/jit-test/tests/cacheir/number-parseInt-string.js144
-rw-r--r--js/src/jit-test/tests/cacheir/number-toString.js44
-rw-r--r--js/src/jit-test/tests/cacheir/object-addprop-hook.js18
-rw-r--r--js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js14
-rw-r--r--js/src/jit-test/tests/cacheir/object-constructor.js76
-rw-r--r--js/src/jit-test/tests/cacheir/object-is-prototype-of.js41
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js13
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js17
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js13
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js36
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js19
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js7
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js10
-rw-r--r--js/src/jit-test/tests/cacheir/optimize-spread.js16
-rw-r--r--js/src/jit-test/tests/cacheir/parseInt-double-truncate.js41
-rw-r--r--js/src/jit-test/tests/cacheir/rope-char-at.js20
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-bigint.js84
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-nongcthing.js110
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-object.js30
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-string-gczeal.js17
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-string.js79
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-symbol.js30
-rw-r--r--js/src/jit-test/tests/cacheir/set-has-value.js30
-rw-r--r--js/src/jit-test/tests/cacheir/set-size.js43
-rw-r--r--js/src/jit-test/tests/cacheir/setelem-id-guard.js91
-rw-r--r--js/src/jit-test/tests/cacheir/setelem-undefined-null.js50
-rw-r--r--js/src/jit-test/tests/cacheir/setgname-let.js93
-rw-r--r--js/src/jit-test/tests/cacheir/setter-is-native.js15
-rw-r--r--js/src/jit-test/tests/cacheir/shape-teleporting-1.js128
-rw-r--r--js/src/jit-test/tests/cacheir/shape-teleporting-2.js47
-rw-r--r--js/src/jit-test/tests/cacheir/shape-teleporting-3.js32
-rw-r--r--js/src/jit-test/tests/cacheir/spread-minmax-1.js23
-rw-r--r--js/src/jit-test/tests/cacheir/spread-minmax-2.js23
-rw-r--r--js/src/jit-test/tests/cacheir/spread-minmax-3.js25
-rw-r--r--js/src/jit-test/tests/cacheir/spread-minmax-4.js15
-rw-r--r--js/src/jit-test/tests/cacheir/spread-minmax-5.js12
-rw-r--r--js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js55
-rw-r--r--js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js55
-rw-r--r--js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js55
-rw-r--r--js/src/jit-test/tests/cacheir/store-dense-element-hole.js43
-rw-r--r--js/src/jit-test/tests/cacheir/store-typed-element-bigint.js131
-rw-r--r--js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js15
-rw-r--r--js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js17
-rw-r--r--js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js20
-rw-r--r--js/src/jit-test/tests/cacheir/string-at-oob.js151
-rw-r--r--js/src/jit-test/tests/cacheir/string-at-rope.js80
-rw-r--r--js/src/jit-test/tests/cacheir/string-at.js115
-rw-r--r--js/src/jit-test/tests/cacheir/string-charAt-oob.js151
-rw-r--r--js/src/jit-test/tests/cacheir/string-charAt-rope.js80
-rw-r--r--js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js151
-rw-r--r--js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js80
-rw-r--r--js/src/jit-test/tests/cacheir/string-codePointAt-oob.js151
-rw-r--r--js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js88
-rw-r--r--js/src/jit-test/tests/cacheir/string-codePointAt-rope.js80
-rw-r--r--js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js103
-rw-r--r--js/src/jit-test/tests/cacheir/string-concat-null-undef.js31
-rw-r--r--js/src/jit-test/tests/cacheir/string-fromCharCode-double.js17
-rw-r--r--js/src/jit-test/tests/cacheir/string-fromcodepoint.js15
-rw-r--r--js/src/jit-test/tests/cacheir/string-int32-arith.js56
-rw-r--r--js/src/jit-test/tests/cacheir/string-lastIndexOf.js102
-rw-r--r--js/src/jit-test/tests/cacheir/string-loadchar.js44
-rw-r--r--js/src/jit-test/tests/cacheir/string-number-arith.js32
-rw-r--r--js/src/jit-test/tests/cacheir/string-toString-valueOf.js23
-rw-r--r--js/src/jit-test/tests/cacheir/stub-fold-closeiter.js25
-rw-r--r--js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js35
-rw-r--r--js/src/jit-test/tests/cacheir/tobool.js95
-rw-r--r--js/src/jit-test/tests/cacheir/topropertykey.js36
-rw-r--r--js/src/jit-test/tests/cacheir/typed-array-intrinsics.js93
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js60
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js53
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js53
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js37
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js37
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js37
-rw-r--r--js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js126
-rw-r--r--js/src/jit-test/tests/cacheir/typeof-proxy.js19
-rw-r--r--js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js26
-rw-r--r--js/src/jit-test/tests/cacheir/unaryarith-string.js58
-rw-r--r--js/src/jit-test/tests/cacheir/unaryarith.js62
-rw-r--r--js/src/jit-test/tests/cacheir/unboxed-element-hole.js41
-rw-r--r--js/src/jit-test/tests/cacheir/windowproxy.js32
228 files changed, 12827 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/cacheir/1877684.js b/js/src/jit-test/tests/cacheir/1877684.js
new file mode 100644
index 0000000000..a926db32fe
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/1877684.js
@@ -0,0 +1,6 @@
+a = 0
+b = [
+ a,
+ new Float64Array
+]
+for (c = 0; c < 10000; ++c) b[c & 1][0] = true
diff --git a/js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js b/js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js
new file mode 100644
index 0000000000..2958ce2283
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/add-dense-element-non-extensible.js
@@ -0,0 +1,160 @@
+// Add dense elements to packed and non-packed arrays. Cover both mono- and
+// polymorphic call sites. Change array to non-extensible during execution.
+
+function testAddDenseEmpty() {
+ var array = [];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseEmpty();
+
+function testAddDensePacked() {
+ var array = [0, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDensePacked();
+
+function testAddDenseNonPacked() {
+ var array = [/* hole */, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ assertEq(0 in array, false);
+ for (var i = 1; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseNonPacked();
+
+function testAddDenseEmptyPoly() {
+ var array = [];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseEmptyPoly();
+
+function testAddDensePackedPoly() {
+ var array = [0, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDensePackedPoly();
+
+function testAddDenseNonPackedPoly() {
+ var array = [/* hole */, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ assertEq(0 in array, false);
+ for (var i = 1; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseNonPackedPoly();
diff --git a/js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js b/js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js
new file mode 100644
index 0000000000..635ee07186
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/add-dense-element-non-writable-length.js
@@ -0,0 +1,160 @@
+// Add dense elements to packed and non-packed arrays. Cover both mono- and
+// polymorphic call sites. Change array length to non-writable during execution.
+
+function testAddDenseEmpty() {
+ var array = [];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseEmpty();
+
+function testAddDensePacked() {
+ var array = [0, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDensePacked();
+
+function testAddDenseNonPacked() {
+ var array = [/* hole */, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ assertEq(0 in array, false);
+ for (var i = 1; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseNonPacked();
+
+function testAddDenseEmptyPoly() {
+ var array = [];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseEmptyPoly();
+
+function testAddDensePackedPoly() {
+ var array = [0, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDensePackedPoly();
+
+function testAddDenseNonPackedPoly() {
+ var array = [/* hole */, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 2; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ assertEq(0 in array, false);
+ for (var i = 1; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testAddDenseNonPackedPoly();
diff --git a/js/src/jit-test/tests/cacheir/add-dense-element.js b/js/src/jit-test/tests/cacheir/add-dense-element.js
new file mode 100644
index 0000000000..099e941648
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/add-dense-element.js
@@ -0,0 +1,124 @@
+// Add dense elements to packed and non-packed arrays. Cover both mono- and
+// polymorphic call sites.
+
+function testAddDenseEmpty() {
+ var array = [];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ store(array, i);
+ }
+
+ assertEq(array.length, 10);
+ for (var i = 0; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testAddDenseEmpty();
+
+function testAddDensePacked() {
+ var array = [0, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 2; i < 10; ++i) {
+ store(array, i);
+ }
+
+ assertEq(array.length, 10);
+ for (var i = 0; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testAddDensePacked();
+
+function testAddDenseNonPacked() {
+ var array = [/* hole */, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 2; i < 10; ++i) {
+ store(array, i);
+ }
+
+ assertEq(array.length, 10);
+ assertEq(0 in array, false);
+ for (var i = 1; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testAddDenseNonPacked();
+
+function testAddDenseEmptyPoly() {
+ var array = [];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 10);
+ for (var i = 0; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testAddDenseEmptyPoly();
+
+function testAddDensePackedPoly() {
+ var array = [0, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 2; i < 10; ++i) {
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 10);
+ for (var i = 0; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testAddDensePackedPoly();
+
+function testAddDenseNonPackedPoly() {
+ var array = [/* hole */, 1];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 2; i < 10; ++i) {
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 10);
+ assertEq(0 in array, false);
+ for (var i = 1; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testAddDenseNonPackedPoly();
diff --git a/js/src/jit-test/tests/cacheir/add-function-prototype.js b/js/src/jit-test/tests/cacheir/add-function-prototype.js
new file mode 100644
index 0000000000..e5a77d75df
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/add-function-prototype.js
@@ -0,0 +1,55 @@
+function checkPrototype(fun, proto, resolvesPrototype) {
+ var desc = Object.getOwnPropertyDescriptor(fun, "prototype");
+ assertEq(desc.value, proto);
+ assertEq(desc.configurable, !resolvesPrototype);
+ assertEq(desc.enumerable, !resolvesPrototype);
+ assertEq(desc.writable, true);
+}
+function addPrototype(fun, proto, resolvesPrototype) {
+ fun.prototype = proto;
+ checkPrototype(fun, proto, resolvesPrototype);
+}
+function test() {
+ for (var i=0; i<50; i++) {
+ addPrototype(function() {}, i, true);
+ addPrototype(function*() {}, i, true);
+ addPrototype(function async() {}, i, true);
+ // Builtins, arrow functions, bound functions don't have a default
+ // prototype property.
+ addPrototype(Math.abs, i, false);
+ addPrototype(Array.prototype.map, i, false);
+ addPrototype(() => 1, i, false);
+ addPrototype((function() {}).bind(null), i, false);
+ }
+
+ // Now test this with a different IC for each function type.
+ for (var i=0; i<50; i++) {
+ var f = function() {};
+ f.prototype = i;
+ checkPrototype(f, i, true);
+
+ f = function*() {};
+ f.prototype = i;
+ checkPrototype(f, i, true);
+
+ f = function async() {};
+ f.prototype = i;
+ checkPrototype(f, i, true);
+
+ Math.sin.prototype = i;
+ checkPrototype(Math.sin, i, false);
+
+ Array.prototype.filter.prototype = i;
+ checkPrototype(Array.prototype.filter, i, false);
+
+ f = () => 1;
+ f.prototype = i;
+ checkPrototype(f, i, false);
+
+ f = (function() {}).bind(null);
+ f.prototype = i;
+ checkPrototype(f, i, false);
+ }
+
+}
+test();
diff --git a/js/src/jit-test/tests/cacheir/alloc-dense-elements.js b/js/src/jit-test/tests/cacheir/alloc-dense-elements.js
new file mode 100644
index 0000000000..3013aa9ec1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/alloc-dense-elements.js
@@ -0,0 +1,9 @@
+function f() {
+ for (var i=0; i<100; i++) {
+ // Int32Array to force an IC in Ion.
+ var o = (i == 20) ? new Int32Array(1) : {};
+ o[0] = i;
+ assertEq(o[0], i);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-1.js b/js/src/jit-test/tests/cacheir/apply-minmax-1.js
new file mode 100644
index 0000000000..3fbf48aba0
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/apply-minmax-1.js
@@ -0,0 +1,23 @@
+function testMin(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.min.apply(null, arr);
+ }
+ return sum;
+}
+
+function testMax(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.max.apply(null, arr);
+ }
+ return sum;
+}
+
+// Attach Int32MinMaxArrayResult.
+assertEq(testMin([1,2,3,4,5]), 50);
+assertEq(testMax([1,2,3,4,5]), 250);
+
+// Verify that we handle an empty list correctly.
+assertEq(testMin([]), Infinity);
+assertEq(testMax([]), -Infinity);
diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-2.js b/js/src/jit-test/tests/cacheir/apply-minmax-2.js
new file mode 100644
index 0000000000..28ff687329
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/apply-minmax-2.js
@@ -0,0 +1,23 @@
+function testMin(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.min.apply(null, arr);
+ }
+ return sum;
+}
+
+function testMax(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.max.apply(null, arr);
+ }
+ return sum;
+}
+
+// Attach Int32MinMaxArrayResult.
+assertEq(testMin([1,2,3,4,5]), 50);
+assertEq(testMax([1,2,3,4,5]), 250);
+
+// Verify that we handle a double element correctly.
+assertEq(testMin([1,2,3.5,4,5]), 50);
+assertEq(testMax([1,2,3.5,4,5]), 250);
diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-3.js b/js/src/jit-test/tests/cacheir/apply-minmax-3.js
new file mode 100644
index 0000000000..3cfad3f958
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/apply-minmax-3.js
@@ -0,0 +1,25 @@
+function testMin(arr) {
+ return Math.min.apply(Math, arr);
+}
+
+function testMax(arr) {
+ return Math.max.apply(Math, arr);
+}
+
+with({}) {}
+
+// Warp-compile.
+var sum = 0;
+for (var i = 0; i < 50; i++) {
+ sum += testMin([1, 2.5, 3]);
+ sum += testMax([1, 2.5, 3]);
+}
+assertEq(sum, 200);
+
+// Test min/max with no arguments.
+assertEq(testMin([]), Infinity);
+assertEq(testMax([]), -Infinity);
+
+// Test NaN.
+assertEq(testMin([1,NaN]), NaN);
+assertEq(testMax([1,NaN]), NaN);
diff --git a/js/src/jit-test/tests/cacheir/apply-minmax-4.js b/js/src/jit-test/tests/cacheir/apply-minmax-4.js
new file mode 100644
index 0000000000..fe001668f9
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/apply-minmax-4.js
@@ -0,0 +1,15 @@
+function f(...rest) {
+ for (var i = 0; i < 100; ++i) {
+ rest[0] = 0;
+ var v = Math.max.apply(Math, rest);
+
+ rest[0] = i;
+ var w = Math.max.apply(Math, rest);
+
+ assertEq(v, 0);
+ assertEq(w, i);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ f(0, 0);
+}
diff --git a/js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js b/js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js
new file mode 100644
index 0000000000..0b6024d9af
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/arguments-iterator-mapped.js
@@ -0,0 +1,171 @@
+// Test iteration with a mapped arguments object.
+
+function simple() {
+ function f() {
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(f(1, 2, 3), 6);
+ }
+}
+simple();
+
+function spreadCall() {
+ function f() {
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ function g() {
+ return f(...arguments);
+ }
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(g(1, 2, 3), 6);
+ }
+}
+spreadCall();
+
+function spreadArray() {
+ function f() {
+ var arr = [...arguments];
+ var sum = 0;
+ for (var v of arr) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(f(1, 2, 3), 6);
+ }
+}
+spreadArray();
+
+function reifyIterator() {
+ var reify = false;
+ function f() {
+ if (reify) {
+ // Redefining any property attributes will reify the iterator property.
+ Object.defineProperty(arguments, Symbol.iterator, {
+ writable: false
+ });
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i <= 100; ++i) {
+ reify = i >= 50;
+ assertEq(f(1, 2, 3), 6);
+ }
+}
+reifyIterator();
+
+function overwriteIterator() {
+ var callCount = 0;
+ function Iterator() {
+ callCount += 1;
+ return Array.prototype[Symbol.iterator].call(this);
+ }
+
+ var overwrite = false;
+ function f() {
+ if (overwrite) {
+ arguments[Symbol.iterator] = Iterator;
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i <= 100; ++i) {
+ overwrite = i > 50;
+ assertEq(f(1, 2, 3), 6);
+ }
+ assertEq(callCount, 50);
+}
+overwriteIterator();
+
+function deleteIterator() {
+ var remove = false;
+ function f() {
+ // Deleting Symbol.iterator won't change the shape of the arguments object.
+ // That's why we need to use a separate guard instruction to check if the
+ // iterator property was modified.
+ if (remove) {
+ delete arguments[Symbol.iterator];
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ var error;
+ try {
+ for (var i = 0; i <= 100; ++i) {
+ remove = i === 100;
+ assertEq(f(1, 2, 3), 6);
+ }
+ } catch (e) {
+ error = e;
+ }
+ assertEq(error instanceof TypeError, true);
+}
+deleteIterator();
+
+function deleteIteratorInherit() {
+ var callCount = 0;
+ function Iterator() {
+ callCount += 1;
+ return Array.prototype[Symbol.iterator].call(this);
+ }
+
+ Object.prototype[Symbol.iterator] = Iterator;
+
+ var remove = false;
+ function f() {
+ // Deleting Symbol.iterator won't change the shape of the arguments object.
+ // That's why we need to use a separate guard instruction to check if the
+ // iterator property was modified.
+ if (remove) {
+ delete arguments[Symbol.iterator];
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i <= 100; ++i) {
+ remove = i === 100;
+ assertEq(f(1, 2, 3), 6);
+ }
+ assertEq(callCount, 1);
+
+ delete Object.prototype[Symbol.iterator];
+}
+deleteIteratorInherit();
+
+// Don't add tests below this point because |Object.prototype[Symbol.iterator]|
+// was modified, which may lead to engine-wide deoptimisations.
diff --git a/js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js b/js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js
new file mode 100644
index 0000000000..29b2163596
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/arguments-iterator-unmapped.js
@@ -0,0 +1,184 @@
+// Test iteration with an unmapped arguments object.
+
+function simple() {
+ function f() {
+ "use strict";
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(f(1, 2, 3), 6);
+ }
+}
+simple();
+
+function spreadCall() {
+ function f() {
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ function g() {
+ "use strict";
+ return f(...arguments);
+ }
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(g(1, 2, 3), 6);
+ }
+}
+spreadCall();
+
+function spreadArray() {
+ function f() {
+ "use strict";
+
+ var arr = [...arguments];
+ var sum = 0;
+ for (var v of arr) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(f(1, 2, 3), 6);
+ }
+}
+spreadArray();
+
+function reifyIterator() {
+ var reify = false;
+ function f() {
+ "use strict";
+
+ if (reify) {
+ // Redefining any property attributes will reify the iterator property.
+ Object.defineProperty(arguments, Symbol.iterator, {
+ writable: false
+ });
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i <= 100; ++i) {
+ reify = i >= 50;
+ assertEq(f(1, 2, 3), 6);
+ }
+}
+reifyIterator();
+
+function overwriteIterator() {
+ var callCount = 0;
+ function Iterator() {
+ callCount += 1;
+ return Array.prototype[Symbol.iterator].call(this);
+ }
+
+ var overwrite = false;
+ function f() {
+ "use strict";
+
+ if (overwrite) {
+ arguments[Symbol.iterator] = Iterator;
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i <= 100; ++i) {
+ overwrite = i > 50;
+ assertEq(f(1, 2, 3), 6);
+ }
+ assertEq(callCount, 50);
+}
+overwriteIterator();
+
+function deleteIterator() {
+ var remove = false;
+ function f() {
+ "use strict";
+
+ // Deleting Symbol.iterator won't change the shape of the arguments object.
+ // That's why we need to use a separate guard instruction to check if the
+ // iterator property was modified.
+ if (remove) {
+ delete arguments[Symbol.iterator];
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ var error;
+ try {
+ for (var i = 0; i <= 100; ++i) {
+ remove = i === 100;
+ assertEq(f(1, 2, 3), 6);
+ }
+ } catch (e) {
+ error = e;
+ }
+ assertEq(error instanceof TypeError, true);
+}
+deleteIterator();
+
+function deleteIteratorInherit() {
+ var callCount = 0;
+ function Iterator() {
+ callCount += 1;
+ return Array.prototype[Symbol.iterator].call(this);
+ }
+
+ Object.prototype[Symbol.iterator] = Iterator;
+
+ var remove = false;
+ function f() {
+ "use strict";
+
+ // Deleting Symbol.iterator won't change the shape of the arguments object.
+ // That's why we need to use a separate guard instruction to check if the
+ // iterator property was modified.
+ if (remove) {
+ delete arguments[Symbol.iterator];
+ }
+
+ var sum = 0;
+ for (var v of arguments) {
+ sum += v;
+ }
+ return sum;
+ }
+
+ for (var i = 0; i <= 100; ++i) {
+ remove = i === 100;
+ assertEq(f(1, 2, 3), 6);
+ }
+ assertEq(callCount, 1);
+
+ delete Object.prototype[Symbol.iterator];
+}
+deleteIteratorInherit();
+
+// Don't add tests below this point because |Object.prototype[Symbol.iterator]|
+// was modified, which may lead to engine-wide deoptimisations.
diff --git a/js/src/jit-test/tests/cacheir/array-slice.js b/js/src/jit-test/tests/cacheir/array-slice.js
new file mode 100644
index 0000000000..2ed43926ff
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/array-slice.js
@@ -0,0 +1,39 @@
+function packed() {
+ var a = [0, 1, 2, 3];
+ for (var i = 0; i <= 100; ++i) {
+ var r = a.slice(0);
+ assertEq(r.length, 4);
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ packed();
+}
+
+function packedThenUnpacked() {
+ var a = [0, 1, 2, 3];
+ var q = 0;
+ for (var i = 0; i <= 100; ++i) {
+ if (i === 100) a[10] = 0;
+
+ var r = a.slice(0);
+ assertEq(r.length, i < 100 ? 4 : 11);
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ packedThenUnpacked();
+}
+
+function unpacked() {
+ var a = [0, 1, /* hole */ , 3];
+ for (var i = 0; i <= 100; ++i) {
+ var r = a.slice(0);
+ assertEq(r.length, 4);
+ assertEq(2 in r, false);
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ unpacked();
+}
diff --git a/js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js b/js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js
new file mode 100644
index 0000000000..3165bd7940
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/atomics-store-non-number-value.js
@@ -0,0 +1,123 @@
+const types = [
+ "Int8",
+ "Int16",
+ "Int32",
+ "Uint8",
+ "Uint16",
+ "Uint32",
+];
+
+function convert(type, value) {
+ let num = Number(value);
+ switch (type) {
+ case "Int8":
+ return ((num | 0) << 24) >> 24;
+ case "Int16":
+ return ((num | 0) << 16) >> 16;
+ case "Int32":
+ return (num | 0);
+ case "Uint8":
+ return (num >>> 0) & 0xff;
+ case "Uint16":
+ return (num >>> 0) & 0xffff;
+ case "Uint32":
+ return (num >>> 0);
+ case "Uint8Clamped": {
+ if (Number.isNaN(num)) {
+ return 0;
+ }
+ let clamped = Math.max(0, Math.min(num, 255));
+ let f = Math.floor(clamped);
+ if (clamped < f + 0.5) {
+ return f;
+ }
+ if (clamped > f + 0.5) {
+ return f + 1;
+ }
+ return f + (f & 1);
+ }
+ case "Float32":
+ return Math.fround(num);
+ case "Float64":
+ return num;
+ }
+ throw new Error();
+}
+
+
+function runTest(type, initial, values) {
+ let expected = values.map(v => convert(type, v));
+ assertEq(
+ expected.some(e => Object.is(e, initial)),
+ false,
+ "initial must be different from the expected values"
+ );
+
+ // Create a fresh function to ensure ICs are specific to a single TypedArray kind.
+ let test = Function("initial, values, expected", `
+ let ta = new ${type}Array(1);
+ for (let i = 0; i < 200; ++i) {
+ ta[0] = initial;
+ Atomics.store(ta, 0, values[i % values.length]);
+ assertEq(ta[0], expected[i % expected.length]);
+ }
+ `);
+ test(initial, values, expected);
+}
+
+const tests = [
+ // |null| is coerced to zero.
+ {
+ initial: 1,
+ values: [null],
+ },
+
+ // |undefined| is coerced to zero or NaN.
+ {
+ initial: 1,
+ values: [undefined],
+ },
+
+ // |false| is coerced to zero and |true| is coerced to one.
+ {
+ initial: 2,
+ values: [false, true],
+ },
+
+ // Strings without a fractional part.
+ {
+ initial: 42,
+ values: [
+ "0", "1", "10", "111", "128", "256", "0x7fffffff", "0xffffffff",
+ ],
+ },
+
+ // Strings without a fractional part, but a leading minus sign.
+ {
+ initial: 42,
+ values: [
+ "-0", "-1", "-10", "-111", "-128", "-256", "-2147483647", "-4294967295",
+ ],
+ },
+
+ // Strings with a fractional number part.
+ {
+ initial: 42,
+ values: [
+ "0.1", "1.2", "10.8", "111.9",
+ "-0.1", "-1.2", "-10.8", "-111.9",
+ ],
+ },
+
+ // Special values and strings not parseable as a number.
+ {
+ initial: 42,
+ values: ["Infinity", "-Infinity", "NaN", "foobar"],
+ },
+];
+
+for (let type of types) {
+ for (let {initial, values} of tests) {
+ runTest(type, initial, values);
+ }
+}
diff --git a/js/src/jit-test/tests/cacheir/bigint-binary.js b/js/src/jit-test/tests/cacheir/bigint-binary.js
new file mode 100644
index 0000000000..773178cc3c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-binary.js
@@ -0,0 +1,190 @@
+var xs = [
+ // Definitely heap digits.
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+];
+
+function testAdd() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = xs[xs.length - 1 - j];
+
+ assertEq(x + 0n, x);
+ assertEq(x + y, 0n);
+ }
+}
+testAdd();
+
+function testSub() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = xs[xs.length - 1 - j];
+
+ assertEq(x - 0n, x);
+ assertEq(x - (-y), 0n);
+ }
+}
+testSub();
+
+function testMul() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x * 0n, 0n);
+ assertEq(x * 1n, x);
+ assertEq(x * (-1n), -x);
+ }
+}
+testMul();
+
+function testDiv() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+
+ // Don't divide by zero.
+ if (j === xs.length >> 1) {
+ assertEq(x / 1n, 0n);
+ continue;
+ }
+
+ assertEq(x / x, 1n);
+ assertEq(x / 1n, x);
+ }
+}
+testDiv();
+
+function testMod() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+
+ // Don't divide by zero.
+ if (j === xs.length >> 1) {
+ assertEq(x / 1n, 0n);
+ continue;
+ }
+
+ assertEq(x % x, 0n);
+ assertEq(x % 1n, 0n);
+ }
+}
+testMod();
+
+function testPow() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x ** 0n, 1n);
+ assertEq(x ** 1n, x);
+ }
+}
+testPow();
+
+function testBitAnd() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x & x, x);
+ assertEq(x & 0n, 0n);
+ }
+}
+testBitAnd();
+
+function testBitOr() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x | x, x);
+ assertEq(x | 0n, x);
+ }
+}
+testBitOr();
+
+function testBitXor() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x ^ x, 0n);
+ assertEq(x ^ 0n, x);
+ }
+}
+testBitXor();
+
+function testLeftShift() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x << 0n, x);
+ assertEq(x << 1n, x * 2n);
+ if (x >= 0n || !(x & 1n)) {
+ assertEq(x << -1n, x / 2n);
+ } else {
+ assertEq(x << -1n, (x / 2n) - 1n);
+ }
+ }
+}
+testLeftShift();
+
+function testRightShift() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ assertEq(x >> 0n, x);
+ if (x >= 0n || !(x & 1n)) {
+ assertEq(x >> 1n, x / 2n);
+ } else {
+ assertEq(x >> 1n, (x / 2n) - 1n);
+ }
+ assertEq(x >> -1n, x * 2n);
+ }
+}
+testRightShift();
diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-double.js b/js/src/jit-test/tests/cacheir/bigint-compare-double.js
new file mode 100644
index 0000000000..ea61e4c558
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-compare-double.js
@@ -0,0 +1,209 @@
+// Same test as bigint-compare-number, except that the inputs are always Double typed.
+
+// Test various combinations of (BigInt R Double) and ensures the result is consistent with
+// (Double R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R Double),
+// but merely ensures the possible combinations are properly handled in CacheIR.
+
+var xs = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+function Double(x) {
+ // numberToDouble always returns a Double valued number.
+ return numberToDouble(x);
+}
+
+// Compute the Double approximation of the BigInt values.
+var ys = xs.map(x => Double(Number(x)));
+
+// Compute if the Double approximation of the BigInt values is exact.
+// (The larger test values are all powers of two, so we can keep this function simple.)
+var zs = xs.map(x => {
+ var isNegative = x < 0n;
+ if (isNegative) {
+ x = -x;
+ }
+ var s = x.toString(2);
+ if (s.length <= 53 || (s.length <= 1024 && /^1+0+$/.test(s))) {
+ return 0;
+ }
+ if (s.length <= 1024 && /^1+$/.test(s)) {
+ return isNegative ? -1 : 1;
+ }
+ if (s.length <= 1024 && /^1+0+1$/.test(s)) {
+ return isNegative ? 1 : -1;
+ }
+ return NaN;
+});
+
+function testLooseEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x == y, z === 0);
+ assertEq(y == x, z === 0);
+ }
+}
+testLooseEqual();
+
+function testLooseNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x != y, z !== 0);
+ assertEq(y != x, z !== 0);
+ }
+}
+testLooseNotEqual();
+
+function testLessThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x < y, false);
+ assertEq(y < x, false);
+ } else if (z > 0) {
+ assertEq(x < y, true);
+ assertEq(y < x, false);
+ } else if (z < 0) {
+ assertEq(x < y, false);
+ assertEq(y < x, true);
+ } else {
+ assertEq(x < y, y > 0);
+ assertEq(y < x, y < 0);
+ }
+ }
+}
+testLessThan();
+
+function testLessThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x <= y, true);
+ assertEq(y <= x, true);
+ } else if (z > 0) {
+ assertEq(x <= y, true);
+ assertEq(y <= x, false);
+ } else if (z < 0) {
+ assertEq(x <= y, false);
+ assertEq(y <= x, true);
+ } else {
+ assertEq(x <= y, y > 0);
+ assertEq(y <= x, y < 0);
+ }
+ }
+}
+testLessThanEquals();
+
+function testGreaterThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x > y, false);
+ assertEq(y > x, false);
+ } else if (z > 0) {
+ assertEq(x > y, false);
+ assertEq(y > x, true);
+ } else if (z < 0) {
+ assertEq(x > y, true);
+ assertEq(y > x, false);
+ } else {
+ assertEq(x > y, y < 0);
+ assertEq(y > x, y > 0);
+ }
+ }
+}
+testGreaterThan();
+
+function testGreaterThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x >= y, true);
+ assertEq(y >= x, true);
+ } else if (z > 0) {
+ assertEq(x >= y, false);
+ assertEq(y >= x, true);
+ } else if (z < 0) {
+ assertEq(x >= y, true);
+ assertEq(y >= x, false);
+ } else {
+ assertEq(x >= y, y < 0);
+ assertEq(y >= x, y > 0);
+ }
+ }
+}
+testGreaterThanEquals();
diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-int32.js b/js/src/jit-test/tests/cacheir/bigint-compare-int32.js
new file mode 100644
index 0000000000..16f6a31ad0
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-compare-int32.js
@@ -0,0 +1,258 @@
+// Extensive test for (BigInt R Int32) comparison operations, testing the output
+// is correct and consistent with (Int32 R⁻¹ BigInt).
+
+function gcd(a, b) {
+ a |= 0;
+ b |= 0;
+ while (b !== 0) {
+ [a, b] = [b, a % b];
+ }
+ return Math.abs(a);
+}
+
+const ITERATIONS = 150;
+
+function assertAllCombinationsTested(xs, ys, n) {
+ // If the array lengths are relatively prime and their product is at least
+ // |n| long, all possible combinations are tested at least once. Make sure
+ // we test each combination at least three times.
+ var m = 3;
+
+ assertEq(gcd(xs.length, ys.length), 1);
+ assertEq(m * xs.length * ys.length <= n, true);
+}
+
+function LessThan(xs, ys, n = ITERATIONS) {
+ assertAllCombinationsTested(xs, ys, n);
+ for (var i = 0; i < n; ++i) {
+ var x = xs[i % xs.length];
+ var y = ys[i % ys.length]|0; // Ensure int32 typed
+
+ assertEq(x == y, false);
+ assertEq(y == x, false);
+
+ assertEq(x != y, true);
+ assertEq(y != x, true);
+
+ assertEq(x < y, true);
+ assertEq(y < x, false);
+
+ assertEq(x <= y, true);
+ assertEq(y <= x, false);
+
+ assertEq(x > y, false);
+ assertEq(y > x, true);
+
+ assertEq(x >= y, false);
+ assertEq(y >= x, true);
+ }
+}
+
+function GreaterThan(xs, ys, n = ITERATIONS) {
+ assertAllCombinationsTested(xs, ys, n);
+ for (var i = 0; i < n; ++i) {
+ var x = xs[i % xs.length];
+ var y = ys[i % ys.length]|0; // Ensure int32 typed
+
+ assertEq(x == y, false);
+ assertEq(y == x, false);
+
+ assertEq(x != y, true);
+ assertEq(y != x, true);
+
+ assertEq(x < y, false);
+ assertEq(y < x, true);
+
+ assertEq(x <= y, false);
+ assertEq(y <= x, true);
+
+ assertEq(x > y, true);
+ assertEq(y > x, false);
+
+ assertEq(x >= y, true);
+ assertEq(y >= x, false);
+ }
+}
+
+function Equal(xs, ys, n = ITERATIONS) {
+ assertAllCombinationsTested(xs, ys, n);
+ for (var i = 0; i < n; ++i) {
+ var x = xs[i % xs.length];
+ var y = ys[i % ys.length]|0; // Ensure int32 typed
+
+ assertEq(x == y, true);
+ assertEq(y == x, true);
+
+ assertEq(x != y, false);
+ assertEq(y != x, false);
+
+ assertEq(x < y, false);
+ assertEq(y < x, false);
+
+ assertEq(x <= y, true);
+ assertEq(y <= x, true);
+
+ assertEq(x > y, false);
+ assertEq(y > x, false);
+
+ assertEq(x >= y, true);
+ assertEq(y >= x, true);
+ }
+}
+
+function test(fn) {
+ // Clone the test function to ensure a new function is compiled each time.
+ return Function(`return ${fn}`)();
+}
+
+const negativeInt32 = [-2147483648, -2147483647, -1];
+const zeroInt32 = [0];
+const positiveInt32 = [1, 2147483646, 2147483647];
+const zeroOrPositiveInt32 = [...zeroInt32, ...positiveInt32];
+const anyInt32 = [...negativeInt32, ...zeroInt32, ...positiveInt32];
+
+// Test when the BigInt is too large to be representable as a single BigInt digit.
+function testLarge() {
+ var xs = [
+ 2n ** 32n, // exceeds single digit limit on 32-bit
+ 2n ** 64n, // exceeds single digit limit on 64-bit
+ 2n ** 96n, // not a single digit on either platform
+ ];
+ test(GreaterThan)(xs, anyInt32);
+
+ var xs = [
+ -(2n ** 32n), // exceeds single digit limit on 32-bit
+ -(2n ** 64n), // exceeds single digit limit on 64-bit
+ -(2n ** 96n), // not a single digit on either platform
+ ];
+ test(LessThan)(xs, anyInt32);
+}
+testLarge();
+
+// Test when the BigInt is 0n.
+function testZero() {
+ var xs = [
+ 0n
+ ];
+
+ test(GreaterThan)(xs, negativeInt32);
+ test(Equal)(xs, zeroInt32);
+ test(LessThan)(xs, positiveInt32);
+}
+testZero();
+
+// Test when both numbers are negative.
+function testNegative() {
+ var xs = [
+ -(2n ** 64n) - 2n,
+ -(2n ** 64n) - 1n, // Max negative using a single BigInt digit on 64-bit.
+ -(2n ** 64n),
+
+ -(2n ** 32n) - 2n,
+ -(2n ** 32n) - 1n, // Max negative using a single BigInt digit on 32-bit.
+ -(2n ** 32n),
+
+ -(2n ** 31n) - 1n, // One past max negative for Int32.
+ ];
+ test(LessThan)(xs, negativeInt32);
+
+ var xs = [
+ -(2n ** 31n), // Max negative for Int32.
+ ];
+ test(Equal)(xs, [-2147483648]);
+ test(LessThan)(xs, [-2147483647, -1]);
+
+ var xs = [
+ -(2n ** 31n) + 1n,
+ ];
+ test(GreaterThan)(xs, [-2147483648]);
+ test(Equal)(xs, [-2147483647]);
+ test(LessThan)(xs, [-1]);
+
+ var xs = [
+ -1n,
+ ];
+ test(GreaterThan)(xs, [-2147483648, -2147483647]);
+ test(Equal)(xs, [-1]);
+}
+testNegative();
+
+// Test when both numbers are positive (and BigInt strictly positive).
+function testPositive() {
+ var xs = [
+ 1n,
+ ];
+ test(GreaterThan)(xs, [0]);
+ test(Equal)(xs, [1]);
+ test(LessThan)(xs, [2147483646, 2147483647]);
+
+ var xs = [
+ 2n ** 31n - 2n,
+ ];
+ test(GreaterThan)(xs, [0, 1]);
+ test(Equal)(xs, [2147483646]);
+ test(LessThan)(xs, [2147483647]);
+
+ var xs = [
+ 2n ** 31n - 1n, // Max positive for Int32.
+ ];
+ test(GreaterThan)(xs, [0, 1, 2147483646]);
+ test(Equal)(xs, [2147483647]);
+
+ var xs = [
+ 2n ** 31n, // One past max positive for Int32.
+
+ 2n ** 32n - 2n,
+ 2n ** 32n - 1n, // Max positive using a single BigInt digit on 32-bit.
+ 2n ** 32n,
+
+ 2n ** 64n - 2n,
+ 2n ** 64n - 1n, // Max positive using a single BigInt digit on 64-bit.
+ 2n ** 64n,
+ ];
+ test(GreaterThan)(xs, zeroOrPositiveInt32);
+}
+testPositive();
+
+// Test negative BigInt and positive Int32.
+function testNegativePositive() {
+ var xs = [
+ -(2n ** 64n) - 2n,
+ -(2n ** 64n) - 1n, // Max negative using a single BigInt digit on 64-bit.
+ -(2n ** 64n),
+
+ -(2n ** 32n) - 2n,
+ -(2n ** 32n) - 1n, // Max negative using a single BigInt digit on 32-bit.
+ -(2n ** 32n),
+
+ -(2n ** 31n) - 1n,
+ -(2n ** 31n), // Max negative for Int32.
+ -(2n ** 31n) + 1n,
+
+ -2n, // Extra entry to ensure assertAllCombinationsTested passes.
+ -1n,
+ ];
+ test(LessThan)(xs, zeroOrPositiveInt32);
+}
+testNegativePositive();
+
+// Test (strictly) positive BigInt and negative Int32.
+function testPositiveNegative() {
+ var xs = [
+ 1n,
+
+ 2n ** 31n - 2n,
+ 2n ** 31n - 1n, // Max positive for Int32.
+ 2n ** 31n,
+
+ 2n ** 32n - 2n,
+ 2n ** 32n - 1n, // Max positive using a single BigInt digit on 32-bit.
+ 2n ** 32n,
+
+ 2n ** 64n - 2n,
+ 2n ** 64n - 1n, // Max positive using a single BigInt digit on 64-bit.
+ 2n ** 64n,
+ ];
+ test(GreaterThan)(xs, negativeInt32);
+}
+testPositiveNegative();
diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js b/js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js
new file mode 100644
index 0000000000..9314b88747
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-compare-null-or-undef.js
@@ -0,0 +1,82 @@
+// Test relational comparison when one operand is null or undefined.
+
+function test(xs) {
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i % xs.length];
+
+ // The result is equal when compared to the result with explicit ToNumber conversions.
+
+ // Test when null-or-undefined is on the right-hand side.
+ assertEq(x < nullOrUndef, x < (+nullOrUndef));
+ assertEq(x <= nullOrUndef, x <= (+nullOrUndef));
+ assertEq(x >= nullOrUndef, x >= (+nullOrUndef));
+ assertEq(x > nullOrUndef, x > (+nullOrUndef));
+
+ // Test when null-or-undefined is on the left-hand side.
+ assertEq(nullOrUndef < x, (+nullOrUndef) < x);
+ assertEq(nullOrUndef <= x, (+nullOrUndef) <= x);
+ assertEq(nullOrUndef >= x, (+nullOrUndef) >= x);
+ assertEq(nullOrUndef > x, (+nullOrUndef) > x);
+ }
+}
+
+function runTest(inputs) {
+ let fNull = Function(`return ${test}`.replaceAll("nullOrUndef", "null"))();
+ fNull(inputs);
+
+ let fUndefined = Function(`return ${test}`.replaceAll("nullOrUndef", "undefined"))();
+ fUndefined(inputs);
+}
+
+// BigInt inputs
+runTest([
+ // Definitely heap digits.
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+]);
diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-number.js b/js/src/jit-test/tests/cacheir/bigint-compare-number.js
new file mode 100644
index 0000000000..9eefaf5053
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-compare-number.js
@@ -0,0 +1,205 @@
+// Same test as bigint-compare-double, except that the inputs are allowed to be any number, i.e.
+// either Int32 or Double.
+
+// Test various combinations of (BigInt R Number) and ensures the result is consistent with
+// (Number R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R Number),
+// but merely ensures the possible combinations are properly handled in CacheIR.
+
+var xs = [
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+];
+
+// Compute the Number approximation of the BigInt values.
+var ys = xs.map(x => Number(x));
+
+// Compute if the Number approximation of the BigInt values is exact.
+// (The larger test values are all powers of two, so we can keep this function simple.)
+var zs = xs.map(x => {
+ var isNegative = x < 0n;
+ if (isNegative) {
+ x = -x;
+ }
+ var s = x.toString(2);
+ if (s.length <= 53 || (s.length <= 1024 && /^1+0+$/.test(s))) {
+ return 0;
+ }
+ if (s.length <= 1024 && /^1+$/.test(s)) {
+ return isNegative ? -1 : 1;
+ }
+ if (s.length <= 1024 && /^1+0+1$/.test(s)) {
+ return isNegative ? 1 : -1;
+ }
+ return NaN;
+});
+
+function testLooseEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x == y, z === 0);
+ assertEq(y == x, z === 0);
+ }
+}
+testLooseEqual();
+
+function testLooseNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x != y, z !== 0);
+ assertEq(y != x, z !== 0);
+ }
+}
+testLooseNotEqual();
+
+function testLessThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x < y, false);
+ assertEq(y < x, false);
+ } else if (z > 0) {
+ assertEq(x < y, true);
+ assertEq(y < x, false);
+ } else if (z < 0) {
+ assertEq(x < y, false);
+ assertEq(y < x, true);
+ } else {
+ assertEq(x < y, y > 0);
+ assertEq(y < x, y < 0);
+ }
+ }
+}
+testLessThan();
+
+function testLessThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x <= y, true);
+ assertEq(y <= x, true);
+ } else if (z > 0) {
+ assertEq(x <= y, true);
+ assertEq(y <= x, false);
+ } else if (z < 0) {
+ assertEq(x <= y, false);
+ assertEq(y <= x, true);
+ } else {
+ assertEq(x <= y, y > 0);
+ assertEq(y <= x, y < 0);
+ }
+ }
+}
+testLessThanEquals();
+
+function testGreaterThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x > y, false);
+ assertEq(y > x, false);
+ } else if (z > 0) {
+ assertEq(x > y, false);
+ assertEq(y > x, true);
+ } else if (z < 0) {
+ assertEq(x > y, true);
+ assertEq(y > x, false);
+ } else {
+ assertEq(x > y, y < 0);
+ assertEq(y > x, y > 0);
+ }
+ }
+}
+testGreaterThan();
+
+function testGreaterThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ if (z === 0) {
+ assertEq(x >= y, true);
+ assertEq(y >= x, true);
+ } else if (z > 0) {
+ assertEq(x >= y, false);
+ assertEq(y >= x, true);
+ } else if (z < 0) {
+ assertEq(x >= y, true);
+ assertEq(y >= x, false);
+ } else {
+ assertEq(x >= y, y < 0);
+ assertEq(y >= x, y > 0);
+ }
+ }
+}
+testGreaterThanEquals();
diff --git a/js/src/jit-test/tests/cacheir/bigint-compare-string.js b/js/src/jit-test/tests/cacheir/bigint-compare-string.js
new file mode 100644
index 0000000000..c5937af6aa
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-compare-string.js
@@ -0,0 +1,189 @@
+// Test various combinations of (BigInt R String) and ensures the result is consistent with
+// (String R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R String),
+// but merely ensures the possible combinations are properly handled in CacheIR.
+
+var xs = [
+ // One before the minimum
+ -(2n ** 2000n) - 1n,
+
+ // Definitely heap digits.
+ -(2n ** 2000n),
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+ 2n ** 2000n,
+
+ // One past the maximum
+ (2n ** 2000n) + 1n,
+];
+
+function idx(i) {
+ return (i % (xs.length - 2)) + 1;
+}
+
+var ys = xs.map(x => String(x));
+
+function testLooseEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = idx(i);
+ var x = xs[j];
+ var y = ys[j];
+ var u = ys[j - 1];
+ var v = ys[j + 1];
+
+ assertEq(x == y, true);
+ assertEq(y == x, true);
+
+ assertEq(x == u, false);
+ assertEq(u == x, false);
+
+ assertEq(x == v, false);
+ assertEq(v == x, false);
+ }
+}
+testLooseEqual();
+
+function testLooseNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = idx(i);
+ var x = xs[j];
+ var y = ys[j];
+ var u = ys[j - 1];
+ var v = ys[j + 1];
+
+ assertEq(x != y, false);
+ assertEq(y != x, false);
+
+ assertEq(x != u, true);
+ assertEq(u != x, true);
+
+ assertEq(x != v, true);
+ assertEq(v != x, true);
+ }
+}
+testLooseNotEqual();
+
+function testLessThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = idx(i);
+ var x = xs[j];
+ var y = ys[j];
+ var u = ys[j - 1];
+ var v = ys[j + 1];
+
+ assertEq(x < y, false);
+ assertEq(y < x, false);
+
+ assertEq(x < u, false);
+ assertEq(u < x, true);
+
+ assertEq(x < v, true);
+ assertEq(v < x, false);
+ }
+}
+testLessThan();
+
+function testLessThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = idx(i);
+ var x = xs[j];
+ var y = ys[j];
+ var u = ys[j - 1];
+ var v = ys[j + 1];
+
+ assertEq(x <= y, true);
+ assertEq(y <= x, true);
+
+ assertEq(x <= u, false);
+ assertEq(u <= x, true);
+
+ assertEq(x <= v, true);
+ assertEq(v <= x, false);
+ }
+}
+testLessThanEquals();
+
+function testGreaterThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = idx(i);
+ var x = xs[j];
+ var y = ys[j];
+ var u = ys[j - 1];
+ var v = ys[j + 1];
+
+ assertEq(x > y, false);
+ assertEq(y > x, false);
+
+ assertEq(x > u, true);
+ assertEq(u > x, false);
+
+ assertEq(x > v, false);
+ assertEq(v > x, true);
+ }
+}
+testGreaterThan();
+
+function testGreaterThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = idx(i);
+ var x = xs[j];
+ var y = ys[j];
+ var u = ys[j - 1];
+ var v = ys[j + 1];
+
+ assertEq(x >= y, true);
+ assertEq(y >= x, true);
+
+ assertEq(x >= u, true);
+ assertEq(u >= x, false);
+
+ assertEq(x >= v, false);
+ assertEq(v >= x, true);
+ }
+}
+testGreaterThanEquals();
diff --git a/js/src/jit-test/tests/cacheir/bigint-compare.js b/js/src/jit-test/tests/cacheir/bigint-compare.js
new file mode 100644
index 0000000000..a67d2722d3
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-compare.js
@@ -0,0 +1,163 @@
+// Test various combinations of (BigInt R BigInt) and ensures the result is consistent with
+// (BigInt R⁻¹ BigInt). This test doesn't aim to cover the overall correctness of (BigInt R BigInt),
+// but merely ensures the possible combinations are properly handled in CacheIR.
+
+var xs = [
+ // Definitely heap digits.
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+];
+
+function testLooseEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isZero = j === xs.length >> 1;
+
+ assertEq(x == x, true);
+ assertEq(x == 0n, isZero);
+ assertEq(0n == x, isZero);
+ }
+}
+testLooseEqual();
+
+function testStrictEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isZero = j === xs.length >> 1;
+
+ assertEq(x === x, true);
+ assertEq(x === 0n, isZero);
+ assertEq(0n === x, isZero);
+ }
+}
+testStrictEqual();
+
+function testLooseNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isZero = j === xs.length >> 1;
+
+ assertEq(x != x, false);
+ assertEq(x != 0n, !isZero);
+ assertEq(0n != x, !isZero);
+ }
+}
+testLooseNotEqual();
+
+function testStrictNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isZero = j === xs.length >> 1;
+
+ assertEq(x !== x, false);
+ assertEq(x !== 0n, !isZero);
+ assertEq(0n !== x, !isZero);
+ }
+}
+testStrictNotEqual();
+
+function testLessThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isNegative = j < (xs.length >> 1);
+ var isPositive = j > (xs.length >> 1);
+
+ assertEq(x < x, false);
+ assertEq(x < 0n, isNegative);
+ assertEq(0n < x, isPositive);
+ }
+}
+testLessThan();
+
+function testLessThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isNegativeOrZero = j <= (xs.length >> 1);
+ var isPositiveOrZero = j >= (xs.length >> 1);
+
+ assertEq(x <= x, true);
+ assertEq(x <= 0n, isNegativeOrZero);
+ assertEq(0n <= x, isPositiveOrZero);
+ }
+}
+testLessThanEquals();
+
+function testGreaterThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isNegative = j < (xs.length >> 1);
+ var isPositive = j > (xs.length >> 1);
+
+ assertEq(x > x, false);
+ assertEq(x > 0n, isPositive);
+ assertEq(0n > x, isNegative);
+ }
+}
+testGreaterThan();
+
+function testGreaterThanEquals() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var isNegativeOrZero = j <= (xs.length >> 1);
+ var isPositiveOrZero = j >= (xs.length >> 1);
+
+ assertEq(x >= x, true);
+ assertEq(x >= 0n, isPositiveOrZero);
+ assertEq(0n >= x, isNegativeOrZero);
+ }
+}
+testGreaterThanEquals();
diff --git a/js/src/jit-test/tests/cacheir/bigint-tobool.js b/js/src/jit-test/tests/cacheir/bigint-tobool.js
new file mode 100644
index 0000000000..806cb55731
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-tobool.js
@@ -0,0 +1,72 @@
+var xs = [
+ // Definitely heap digits.
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483649n,
+ 2147483648n,
+ 2147483647n,
+
+ // 2**32
+ 4294967297n,
+ 4294967296n,
+ 4294967295n,
+
+ // 2n**63n
+ 9223372036854775809n,
+ 9223372036854775808n,
+ 9223372036854775807n,
+
+ // 2n**64n
+ 18446744073709551617n,
+ 18446744073709551616n,
+ 18446744073709551615n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+];
+
+function test() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ // Implicit ToBool(x)
+ var r = x ? true : false;
+ assertEq(r, x !== 0n);
+ }
+}
+test();
+
+function testNot() {
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i % xs.length];
+
+ var r = !x;
+ assertEq(r, x === 0n);
+ }
+}
+testNot();
diff --git a/js/src/jit-test/tests/cacheir/bigint-unary.js b/js/src/jit-test/tests/cacheir/bigint-unary.js
new file mode 100644
index 0000000000..75e67a7d53
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bigint-unary.js
@@ -0,0 +1,135 @@
+var xs = [
+ // Definitely heap digits.
+ -(2n ** 1000n),
+
+ // -(2n**64n)
+ -18446744073709551617n,
+ -18446744073709551616n,
+ -18446744073709551615n,
+
+ // -(2n**63n)
+ -9223372036854775809n,
+ -9223372036854775808n,
+ -9223372036854775807n,
+
+ // -(2**32)
+ -4294967297n,
+ -4294967296n,
+ -4294967295n,
+
+ // -(2**31)
+ -2147483649n,
+ -2147483648n,
+ -2147483647n,
+
+ -1n,
+ 0n,
+ 1n,
+
+ // 2**31
+ 2147483647n,
+ 2147483648n,
+ 2147483649n,
+
+ // 2**32
+ 4294967295n,
+ 4294967296n,
+ 4294967297n,
+
+ // 2n**63n
+ 9223372036854775807n,
+ 9223372036854775808n,
+ 9223372036854775809n,
+
+ // 2n**64n
+ 18446744073709551615n,
+ 18446744073709551616n,
+ 18446744073709551617n,
+
+ // Definitely heap digits.
+ 2n ** 1000n,
+];
+
+function testNeg() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = xs[xs.length - 1 - j];
+
+ assertEq(-x, y);
+ }
+}
+testNeg();
+
+function testBitNot() {
+ var ys = xs.map(x => -(x + 1n));
+
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ assertEq(~x, y);
+ }
+}
+testBitNot();
+
+function testPreInc() {
+ var ys = xs.map(x => x + 1n);
+
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ var r = ++x;
+ assertEq(x, y);
+ assertEq(r, y);
+ }
+}
+testPostInc();
+
+function testPostInc() {
+ var ys = xs.map(x => x + 1n);
+
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ var r = x++;
+ assertEq(x, y);
+ assertEq(r, xs[j]);
+ }
+}
+testPostInc();
+
+function testPreDec() {
+ var ys = xs.map(x => x - 1n);
+
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ var r = --x;
+ assertEq(x, y);
+ assertEq(r, y);
+ }
+}
+testPostDec();
+
+function testPostDec() {
+ var ys = xs.map(x => x - 1n);
+
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ var r = x--;
+ assertEq(x, y);
+ assertEq(r, xs[j]);
+ }
+}
+testPostDec();
diff --git a/js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js b/js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js
new file mode 100644
index 0000000000..0801606808
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/binaryarith-mod-int32.js
@@ -0,0 +1,70 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+function runTest(dividend, divisor) {
+ function test(dividend, divisor, expected) {
+ for (var i = 0; i < dividend.length; i++) {
+ var x = dividend[i];
+ for (var j = 0; j < divisor.length; j++) {
+ var y = divisor[j];
+ var result = x % y;
+
+ assertEq(result, expected[i * divisor.length + j]);
+ }
+ }
+ }
+
+ var f = Function(`return ${test}`)();
+
+ // Seed the IC to ensure we're using Int32Mod.
+ var ones = Array(8).fill(1);
+ var zeros = Array(8 * 8).fill(0);
+ for (var i = 0; i < 2; ++i) {
+ f(ones, ones, zeros);
+ }
+
+ var expected = dividend.map(x => divisor.map(y => x % y)).flat();
+ for (var i = 0; i < 10; ++i) {
+ f(dividend, divisor, expected);
+ }
+}
+
+const positiveInt32 = [
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 2**31 - 8,
+ 2**31 - 7,
+ 2**31 - 6,
+ 2**31 - 5,
+ 2**31 - 4,
+ 2**31 - 3,
+ 2**31 - 2,
+ 2**31 - 1,
+];
+
+const negativeInt32 = [
+ -1, -2, -3, -4, -5, -6, -7, -8,
+ -(2**31 - 8),
+ -(2**31 - 7),
+ -(2**31 - 6),
+ -(2**31 - 5),
+ -(2**31 - 4),
+ -(2**31 - 3),
+ -(2**31 - 2),
+ -(2**31 - 1),
+ -(2**31),
+];
+
+const zero = [0];
+
+const cases = [positiveInt32, zero, negativeInt32];
+
+for (let a of cases) {
+ for (let b of cases) {
+ runTest(a, b);
+ }
+}
+
+// Test some "interesting" cases.
+
+// |-2147483648 % -1| may internally compute |-2147483648 / -1|, which isn't an int32 and
+// may trigger a floating point exception on some architectures.
+runTest([-2147483648], [-1]);
diff --git a/js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js b/js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js
new file mode 100644
index 0000000000..140db26572
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/binaryarith-null-undef-bool.js
@@ -0,0 +1,75 @@
+// Pass |null| as argument to be more robust against code folding.
+function testNullWithInt32OrBool(nullVal) {
+ var vals = [0, 1, true, false, null];
+ for (var v of vals) {
+ assertEq(v + nullVal, Number(v));
+ assertEq(v - nullVal, Number(v));
+ assertEq(v * nullVal, 0);
+ var res = v / nullVal;
+ assertEq(isNaN(res) || res === Infinity, true);
+ assertEq(v % nullVal, NaN);
+ assertEq(v ** nullVal, 1);
+
+ assertEq(nullVal + v, Number(v));
+ assertEq(nullVal - v, 0 - Number(v));
+ assertEq(nullVal * v, 0);
+ res = nullVal / v;
+ assertEq(isNaN(res) || res === 0, true);
+ res = nullVal % v;
+ assertEq(isNaN(res) || res === 0, true);
+ res = nullVal ** v;
+ assertEq(res === 0 || res === 1, true);
+ }
+}
+for (var i = 0; i < 15; i++) {
+ testNullWithInt32OrBool(null);
+}
+
+function testUndefinedWithOther(undefinedVal) {
+ var vals = [1.1, NaN, true, false, null, undefined];
+ for (var v of vals) {
+ assertEq(v + undefinedVal, NaN);
+ assertEq(v - undefinedVal, NaN);
+ assertEq(v * undefinedVal, NaN);
+ assertEq(v / undefinedVal, NaN);
+ assertEq(v % undefinedVal, NaN);
+ assertEq(v ** undefinedVal, NaN);
+
+ assertEq(undefinedVal + v, NaN);
+ assertEq(undefinedVal - v, NaN);
+ assertEq(undefinedVal * v, NaN);
+ assertEq(undefinedVal / v, NaN);
+ assertEq(undefinedVal % v, NaN);
+ var res = undefinedVal ** v;
+ if (v === false || v === null) {
+ assertEq(res, 1);
+ } else {
+ assertEq(res, NaN);
+ }
+ }
+}
+for (var i = 0; i < 15; i++) {
+ testUndefinedWithOther(undefined);
+}
+
+function testBooleanWithDouble(trueVal, falseVal) {
+ var vals = [1.1, 2.2, 5, 6, 3.14];
+ for (var v of vals) {
+ assertEq(v + falseVal, v);
+ assertEq(v - falseVal, v);
+ assertEq(v * falseVal, 0);
+ assertEq(v / falseVal, Infinity);
+ assertEq(v % falseVal, NaN);
+ assertEq(v ** falseVal, 1);
+
+ assertEq(trueVal + v, v + 1);
+ assertEq(trueVal - v, 1 - v);
+ assertEq(trueVal * v, v);
+ assertEq(trueVal / v, 1 / v);
+ assertEq(trueVal % v, 1);
+ assertEq(trueVal ** v, 1);
+ }
+}
+for (var i = 0; i < 15; i++) {
+ testBooleanWithDouble(true, false);
+}
diff --git a/js/src/jit-test/tests/cacheir/binaryarith.js b/js/src/jit-test/tests/cacheir/binaryarith.js
new file mode 100644
index 0000000000..1746aad963
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/binaryarith.js
@@ -0,0 +1,322 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function warmup(fun, input_array) {
+ for (var index = 0; index < input_array.length; index++) {
+ input = input_array[index];
+ input_lhs = input[0];
+ input_rhs = input[1];
+ output = input[2];
+ for (var i = 0; i < 30; i++) {
+ var y = fun(input_lhs, input_rhs);
+ assertEq(y, output)
+ }
+ }
+}
+
+
+// Add: Int32 + Int32 Overflow
+var funAdd1 = (a, b) => { return a + b; }
+warmup(funAdd1, [[1,2, 3], [3,4, 7], [4294967295, 2, 4294967297]]);
+
+// Add: Doubles
+var funAdd2 = (a, b) => { return a + b; }
+warmup(funAdd2, [[1.2, 2, 3.2], [3.5, 4, 7.5], [4294967295.1, 2, 4294967297.1]]);
+
+// Add: Type Change Int32 -> Double
+var funAdd3 = (a, b) => { return a + b; }
+warmup(funAdd3, [[1, 2, 3], [3, 4, 7], [4294967295, 2, 4294967297], [1.2, 2, 3.2]]);
+
+//Add: String Concat
+var funAdd4 = (a, b) => { return a + b; }
+warmup(funAdd4, [["","a","a"], ["ab","ba","abba"], ["123","456","123456"]])
+
+function D(name) { this.name = name; }
+D.prototype.toString = function stringify() {
+ return this.name;
+}
+obj1 = new D('A');
+
+// Add: String Object Concat
+var funAdd4 = (a, b) => { return a + b; }
+warmup(funAdd4, [["x", obj1, "xA"], [obj1, "bba", "Abba"]]);
+
+// Add: Int32 Boolean
+var funAdd5 = (a, b) => { return a + b; }
+warmup(funAdd5, [[true, 10, 11], [false, 1, 1], [10, true, 11], [1, false, 1],
+ [2147483647, true, 2147483648],[true, 2147483647, 2147483648]]);
+
+// Add: String Number Concat
+var funAdd6 = (a, b) => { return a + b; }
+warmup(funAdd6, [["x", 10, "x10"], [10, "bba", "10bba"], ["x", 1.2, "x1.2"],
+ [1.2, "bba", "1.2bba"]]);
+
+// Add: String Boolean
+var funAddStrBool = (a, b) => { return a + b; }
+warmup(funAddStrBool, [[true, "true", "truetrue"], [false, "true", "falsetrue"],
+ ["a string", true, "a stringtrue"]]);
+
+// Sub Int32
+var funSub1 = (a, b) => { return a - b; }
+warmup(funSub1, [[7, 0, 7], [7, 8, -1], [4294967295, 2, 4294967293], [0,0,0]]);
+
+// Sub Double
+var funSub2 = (a, b) => { return a - b; }
+warmup(funSub2, [[7.5, 0, 7.5], [7, 8.125, -1.125], [4294967295.3125, 2, 4294967293.3125], [NaN,10,NaN]]);
+
+// Sub Int32 + Boolean
+var funSub3 = (a, b) => { return a - b; }
+warmup(funSub3, [[7, false, 7], [7, true, 6], [false, 1, -1], [true,1,0]]);
+
+
+
+// Mul: Int32+ Int32 Overflow
+var funMul1 = (a, b) => { return a * b; }
+warmup(funMul1, [[1, 2, 2], [10, 21, 210], [3, 4, 12], [2147483649, 2, 4294967298], [1073741824, 1024, 1099511627776 ]]);
+
+// Mul: Doubles
+var funMul2 = (a, b) => { return a * b; }
+warmup(funMul2, [[3/32, 32, 3], [16/64, 32, 8], [3.0, 1.0, 3], [-1, 0, -0], [0, -20, -0]]);
+
+// Mul: Type change Int32 -> Double
+var funMul3 = (a, b) => { return a * b; }
+warmup(funMul3, [[1,2, 2], [10, 21, 210], [3, 4, 12], [63/32, 32, 63], [16/64, 32, 8]]);
+
+// Mul: Boolean
+var funMul1 = (a, b) => { return a * b; }
+warmup(funMul1, [[1, true, 1], [10, false, 0], [false, 4, 0], [2147483640, true, 2147483640]]);
+
+//Div: Int32
+var funDiv1 = (a,b) => { return a / b;}
+warmup(funDiv1,[[8, 4, 2], [16, 32, 0.5], [10, 0, Infinity], [0, 0, NaN]]);
+
+//Div: Double
+var funDiv2 = (a,b) => { return a / b;}
+warmup(funDiv2, [[8.8, 4, 2.2], [16.8, 32, 0.525], [10, 0.5, 20]]);
+
+//Div: Type change Int32 -> Double
+var funDiv3 = (a,b) => { return a / b;}
+warmup(funDiv1, [[8, 4, 2], [16, 32, 0.5], [10, 0, Infinity], [0, 0, NaN], [8.8, 4, 2.2],
+ [16.8, 32, 0.525], [10, 0.5, 20]]);
+
+//Div: Boolean w/ Int32
+var funDiv4 = (a,b) => { return a / b;}
+warmup(funDiv4,[[8, true, 8], [true, 2, 0.5], [10, false, Infinity], [false, false, NaN]]);
+
+//Mod: Int32
+var funMod1 = (a,b) => {return a % b};
+warmup(funMod1, [[8, 4, 0], [9, 4, 1], [-1, 2, -1], [4294967297, 2, 1],
+ [10, -3, 1]]);
+
+//Mod: Double
+var funMod2 = (a,b) => {return a % b};
+warmup(funMod2, [[8.5, 1, 0.5], [9.5, 0.5, 0], [-0.03125, 0.0625, -0.03125], [1.64, 1.16, 0.48]]);
+
+//Mod: Type change Int32 -> Double
+var funMod3 = (a,b) => {return a % b};
+warmup(funMod3, [[10, 0, NaN], [8, 4, 0], [9, 4, 1], [-1, 2, -1], [4294967297, 2, 1],
+ [8.5, 1, 0.5], [9.5, 0.5, 0], [-0.03125, 0.0625, -0.03125],
+ [1.64, 1.16, 0.48]]);
+
+//Mod: Boolean w/ Int32
+var funMod4 = (a,b) => {return a % b};
+warmup(funMod4, [[10, false, NaN], [8, true, 0], [false, 4, 0], [true, 2, 1]]);
+
+//Pow: Int32
+var funPow1 = (a,b) => {return a ** b};
+warmup(funPow1, [[8, 4, 4096], [9, 4, 6561], [-1, 2, 1], [2, -10000, 0],
+ [-3, 3, -27]]);
+
+//Pow: Double
+var funPow2 = (a,b) => {return a ** b};
+warmup(funPow2, [[8.5, 1, 8.5], [16, 0.5, 4], [4.5, 5, 1845.28125], [18.0625, 0.5, 4.25],
+ [4, -1, 0.25]]);
+
+//Pow: Type change Int32 -> Double
+var funPow3 = (a,b) => {return a ** b};
+warmup(funPow3, [[10, 0, 1], [8, 4, 4096], [9, 4, 6561], [-1, 2, 1], [2, -10000, 0],
+ [8.5, 1, 8.5], [16, 0.5, 4], [4.5, 5, 1845.28125], [18.0625, 0.5, 4.25],
+ [4, -1, 0.25]]);
+
+//Pow: Boolean w/ Int32
+var funPow4 = (a,b) => {return a ** b};
+warmup(funPow4, [[10, 2, 100], [8, true, 8], [false, 4, 0], [true, 2, 1]]);
+
+//BitOr Int32
+var funBitOr1 = (a, b) => { return a | b; }
+warmup(funBitOr1, [[1, 1, 1], [8, 1, 9], [0, 1233, 1233], [5, 0, 5],
+ [4294967295, 123231, -1], [2147483647, 1243524, 2147483647]]);
+
+//BitOr Boolean w/ Int32
+var funBitOr3 = (a, b) => { return a | b; }
+warmup(funBitOr3, [[1, true, 1], [8, true, 9], [false, 1233, 1233], [5, false, 5]]);
+
+//BitOr null w/ Int32
+var funBitOr4 = (a, b) => { return a | b; }
+warmup(funBitOr4, [[1, null, 1], [8, null, 8], [null, 1233, 1233], [5, null, 5]]);
+
+//BitOr undefined w/ Int32
+var funBitOr5 = (a, b) => { return a | b; }
+warmup(funBitOr5, [[1, void 0, 1], [8, void 0, 8], [void 0, 1233, 1233], [5, void 0, 5]]);
+
+//BitXOr Int32
+var funBitXOr1 = (a, b) => { return a ^ b; }
+warmup(funBitXOr1, [[1, 1, 0], [5, 1, 4], [63, 31, 32], [4294967295, 2147483647, -2147483648 ] ]);
+
+//BitXOr Int32
+var funBitXOr2 = (a, b) => { return a ^ b; }
+warmup(funBitXOr2, [[1, true, 0], [5, true, 4], [5, false, 5], [false, 1, 1]]);
+
+//BitXOr Double+int32
+var funBitXOr3 = (a, b) => { return a ^ b; }
+warmup(funBitXOr3, [[1.3, 1, 0], [5, 1.4, 4], [63.1, 31, 32], [4294967295.9, 2147483647, -2147483648 ] ]);
+
+//BitXOr Number Number
+var funBitXOr4 = (a, b) => { return a ^ b; }
+warmup(funBitXOr4, [[54772703898, 2890608493, 1828589047],
+ [-54772703898, 2890608493, -1828589045],
+ [18446744073709551615, 54772703898, -1061870950], //UINT64 Max
+ [-18446744073709551615, 54772703898, -1061870950],
+ [4294967295, -1, 0]]);
+
+//BitXOr null+int32
+var funBitXOr5 = (a, b) => { return a ^ b; }
+warmup(funBitXOr5, [[1, null, 1], [5, null, 5], [5, null, 5], [null, 1, 1]]);
+
+//BitXOr undefined+int32
+var funBitXOr6 = (a, b) => { return a ^ b; }
+warmup(funBitXOr6, [[1, void 0, 1], [5, void 0, 5], [5, void 0, 5], [void 0, 1, 1]]);
+
+//BitAnd Int32
+var funBitAnd1 = (a, b) => { return a & b; }
+warmup(funBitAnd1, [[1,1,1], [5,1,1], [63,31,31], [4294967295,2147483647,2147483647],
+ [-2,10,10], [-15,-2,-16], [4,128,0]]);
+//BitAnd Double w/ Int32
+var funBitAnd2 = (a, b) => { return a & b; }
+warmup(funBitAnd2, [[1.2 ,1, 1], [5, 1.4, 1], [63,31.99,31],
+ [4294967295.98,2147483647,2147483647],
+ [-2.9, 10, 10], [-15,-2.9,-16], [4,128.01,0]]);
+
+//BitAnd Int32 + Boolean
+var funBitAnd1 = (a, b) => { return a & b; }
+warmup(funBitAnd1, [[1,true,1], [5,false,0], [true, 6, 0], [false, 12, 0]]);
+
+//BitAnd Int32 + null
+var funBitAnd4 = (a, b) => { return a & b; }
+warmup(funBitAnd4, [[1, null, 0], [5, null, 0], [null, 6, 0], [null, 12, 0]]);
+
+//BitAnd Int32 + undefined
+var funBitAnd5 = (a, b) => { return a & b; }
+warmup(funBitAnd5, [[1, void 0, 0], [5, void 0, 0], [void 0, 6, 0], [void 0, 12, 0]]);
+
+//Lsh Int32
+var funLsh1 = (a, b) => { return a << b; }
+warmup(funLsh1, [[5,1,10], [1,1,2], [63,31,-2147483648],
+ [4294967295,2147483647,-2147483648], [-2,10,-2048], [-15,-2,1073741824],
+ [4,128,4], [1,10,1024], [1024,2,4096]]);
+
+//Lsh Boolean w/ Int32
+var funLsh2 = (a, b) => { return a << b; }
+warmup(funLsh2, [[5,true,10], [true,1,2], [63,false,63], [false, 12, 0]]);
+
+//Lsh Number Number
+var funLsh3 = (a, b) => { return a << b; }
+warmup(funLsh3, [[54772703898, 1, -2123741900],[2147483658, 0, -2147483638]]);
+
+//Lsh Boolean w/ null
+var funLsh4 = (a, b) => { return a << b; }
+warmup(funLsh4, [[5, null, 5], [null, 1, 0], [63, null, 63], [null, 12, 0]]);
+
+//Lsh Boolean w/ undefined
+var funLsh5 = (a, b) => { return a << b; }
+warmup(funLsh5, [[5, void 0, 5], [void 0, 1, 0], [63, void 0, 63], [void 0, 12, 0]]);
+
+//Rsh Int32
+var funRsh1 = (a, b) => { return a >> b; }
+warmup(funRsh1, [[1,1,0], [5,1,2], [63,31,0], [4294967295,2147483647,-1], [-2,10,-1],
+ [-15,-2,-1], [4,128,4], [1,10,0], [1024,2,256]]);
+
+//Rsh Int32
+var funRsh2 = (a, b) => { return a >> b; }
+warmup(funRsh2, [[true,1,0], [1,true,0], [false, 3, 0], [3, false, 3]]);
+
+//Rsh Number Number
+var funRsh3 = (a, b) => { return a >> b; }
+warmup(funRsh3, [[54772703898, 11, -518492 ], [2147483658, 0, -2147483638]]);
+
+//Rsh Int32 null
+var funRsh4 = (a, b) => { return a >> b; }
+warmup(funRsh4, [[null, 1, 0], [1, null, 1], [null, 3, 0], [3, null, 3]]);
+
+//Rsh Int32 undefined
+var funRsh5 = (a, b) => { return a >> b; }
+warmup(funRsh5, [[void 0, 1, 0], [1, void 0, 1], [void 0, 3, 0], [3, void 0, 3]]);
+
+//URsh Int32
+var funURsh1 = (a, b) => { return a >>> b; }
+warmup(funURsh1, [[1,1,0], [5,1,2], [63,31,0], [4294967295,2147483647,1], [-2,10,4194303],
+ [-15,-2,3], [4,128,4], [1,10,0], [1024,2,256], [0, -6, 0], [0, 6, 0],
+ [0x55005500, 2, 0x15401540]]);
+
+//URsh Boolean Int32
+var funURsh2 = (a, b) => { return a >>> b; }
+warmup(funURsh2, [[true,1,0], [5,true,2], [63,false,63], [false, 20, 0]]);
+
+//URsh Int32
+var funURsh3 = (a, b) => { return a >>> b; }
+warmup(funURsh3, [[4294967295, 0, 4294967295]]);
+
+//URsh Number Number
+var funURsh4 = (a, b) => { return a >>> b; }
+warmup(funURsh4, [[54772703898, 11, 1578660], [2147483658, 11, 1048576],
+ [4294967295, 0, 4294967295]]);
+
+//URsh null Int32
+var funURsh5 = (a, b) => { return a >>> b; }
+warmup(funURsh5, [[null, 1, 0], [5, null, 5], [63, null, 63], [null, 20, 0]]);
+
+//URsh undefined Int32
+var funURsh6 = (a, b) => { return a >>> b; }
+warmup(funURsh6, [[void 0, 1, 0], [5, void 0, 5], [63, void 0, 63], [void 0, 20, 0]]);
+
+
+// Other Test cases that Have been useful:
+for (var k=0; k < 30; k++) {
+ A="01234567";
+ res =""
+ for (var i = 0; i < 8; ++i) {
+ var v = A[7 - i];
+ res+=v;
+ }
+ assertEq(res, "76543210");
+}
+
+// Begin OOM testing:
+if (!('oomTest' in this))
+ quit();
+
+// Add: String Number Concat OOM test
+var addOom = (a, b) => { return a + b; }
+
+function generate_digits(prefix, start) {
+ digits = []
+ number = ""+start+".25";
+ for (var i = 1; i < 7; i++) {
+ number = i + number;
+ digits.push([prefix, Number(number), prefix + number]);
+ }
+ return digits;
+}
+
+// Trying to defeat dtoacache: Warm the IC with one set of digits, then actually oomTest
+// using another set.
+var warmup_digits = generate_digits("x", 1);
+var test_digits = generate_digits("x", 2);
+
+function ot(digits) {
+ warmup(addOom, digits);
+}
+
+// Ensure ICs are warmed
+ot(warmup_digits);
+
+oomTest(() => { ot(test_digits); }); \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/bind-function-specialized.js b/js/src/jit-test/tests/cacheir/bind-function-specialized.js
new file mode 100644
index 0000000000..acac842a7f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bind-function-specialized.js
@@ -0,0 +1,72 @@
+load(libdir + "asserts.js");
+
+function testBasic() {
+ var g = function foo(a, b, c) { return a - b - c; };
+ for (var i = 0; i < 100; i++) {
+ var bound1 = g.bind(null, 1);
+ assertEq(bound1.length, 2);
+ assertEq(bound1.name, "bound foo");
+ var bound2 = bound1.bind(null, 2);
+ assertEq(bound2.length, 1);
+ assertEq(bound2.name, "bound bound foo");
+ assertEq(bound2(9), -10);
+ }
+}
+testBasic();
+
+function testBindNonCtor() {
+ var g = (a, b, c) => a - b - c;
+ for (var i = 0; i < 100; i++) {
+ var bound1 = g.bind(null, 1);
+ var bound2 = bound1.bind(null, 2);
+ assertEq(bound1(2, 3), -4);
+ assertEq(bound2(4), -5);
+ assertThrowsInstanceOf(() => new bound1(2, 3), TypeError);
+ assertThrowsInstanceOf(() => new bound2(4), TypeError);
+ assertEq(bound2.length, 1);
+ assertEq(bound2.name, "bound bound g");
+ }
+}
+testBindNonCtor();
+
+function testBindSelfHosted() {
+ var g = Array.prototype.map;
+ var arr = [1, 2, 3];
+ for (var i = 0; i < 100; i++) {
+ var bound1 = g.bind(arr);
+ var bound2 = bound1.bind(null, x => x + 5);
+ assertEq(bound1(x => x + 3).toString(), "4,5,6");
+ assertEq(bound2().toString(), "6,7,8");
+ assertEq(bound2.length, 0);
+ assertEq(bound2.name, "bound bound map");
+ }
+}
+testBindSelfHosted();
+
+function testBoundDeletedName() {
+ var g = function foo(a, b, c) { return a - b - c; };
+ var bound1 = g.bind(null);
+ var bound2 = g.bind(null);
+ delete bound2.name;
+ for (var i = 0; i < 100; i++) {
+ var obj = i < 50 ? bound1 : bound2;
+ var bound3 = obj.bind(null);
+ assertEq(bound3.length, 3);
+ assertEq(bound3.name, i < 50 ? "bound bound foo" : "bound ");
+ }
+}
+testBoundDeletedName();
+
+function testWeirdProto() {
+ var g = function foo() { return 123; };
+ var proto = {bind: Function.prototype.bind};
+ Object.setPrototypeOf(g, proto);
+ for (var i = 0; i < 100; i++) {
+ var bound1 = g.bind(null);
+ assertEq(Object.getPrototypeOf(bound1), proto);
+ var bound2 = bound1.bind(null);
+ assertEq(Object.getPrototypeOf(bound2), proto);
+ assertEq(bound2(), 123);
+ }
+}
+testWeirdProto();
diff --git a/js/src/jit-test/tests/cacheir/bindname-lexical-errors.js b/js/src/jit-test/tests/cacheir/bindname-lexical-errors.js
new file mode 100644
index 0000000000..d31eecdafb
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bindname-lexical-errors.js
@@ -0,0 +1,26 @@
+const x = 1;
+function testConst() {
+ for (var i = 0; i < 20; i++) {
+ try {
+ x = 2;
+ } catch (e) {
+ continue;
+ }
+ throw "Fail1";
+ }
+ assertEq(x, 1);
+}
+testConst();
+
+function testUninit() {
+ for (var i = 0; i < 20; i++) {
+ try {
+ y = 2;
+ } catch (e) {
+ continue;
+ }
+ throw "Fail2";
+ }
+}
+testUninit();
+let y;
diff --git a/js/src/jit-test/tests/cacheir/boolean-call.js b/js/src/jit-test/tests/cacheir/boolean-call.js
new file mode 100644
index 0000000000..7c6d94f3d4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/boolean-call.js
@@ -0,0 +1,67 @@
+function wrapper(values) {
+ function test(values) {
+ var expected = values.map(v => !!v);
+
+ for (var i = 0; i < 100; ++i) {
+ var ix = i % values.length;
+ var val = values[ix];
+ var actual = Boolean(val);
+ assertEq(actual, expected[ix]);
+ }
+ }
+
+ for (var i = 0; i < 2; ++i) {
+ test(values);
+ }
+}
+
+function makeTest() {
+ // Create a copy to avoid type pollution.
+ return Function(`return ${wrapper}`)();
+}
+
+// Use a new compartment to create a wrapper.
+var g = newGlobal({newCompartment: true});
+
+var testValues = {
+ boolean: [true, false],
+ int32: [-2147483648, -1, 0, 1, 2147483647],
+ double: [-Infinity, -1.5, -1, -0.5, -0, +0, +0.5, +1, +1.5, Infinity, NaN],
+ string: ["", "true", "false", "0", "1", "hello"],
+ symbol: [Symbol(), Symbol("desc"), Symbol.iterator],
+ bigint: [
+ -(2n ** 1000n),
+ -18446744073709551616n, // -(2n**64n)
+ -9223372036854775808n, // -(2n**63n)
+ -4294967296n, // -(2**32)
+ -2147483648n, // -(2**31)
+ -1n, 0n, 1n,
+ 2147483648n, // 2**31
+ 4294967296n, // 2**32
+ 9223372036854775808n, // 2n**63n
+ 18446744073709551616n, // 2n**64n
+ 2n ** 1000n,
+ ],
+ object: [{}, [], function(){}, new Proxy({}, {}), createIsHTMLDDA(), g.eval("createIsHTMLDDA()")],
+ null: [null],
+ undefined: [undefined],
+};
+
+for (var values of Object.values(testValues)) {
+ makeTest()(values);
+}
+
+// boolean and int32
+makeTest()([].concat(testValues.boolean, testValues.int32));
+
+// int32 and double
+makeTest()([].concat(testValues.int32, testValues.double));
+
+// null and undefined
+makeTest()([].concat(testValues.null, testValues.undefined));
+
+// null, undefined, and object
+makeTest()([].concat(testValues.null, testValues.undefined, testValues.object));
+
+// all values
+makeTest()(Object.values(testValues).flat());
diff --git a/js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js b/js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js
new file mode 100644
index 0000000000..13da6000f1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/boolean-compare-string-or-double.js
@@ -0,0 +1,149 @@
+// Test comparison between Boolean x {String, Double}.
+
+// Boolean R String <=> ToNumber(Boolean) R ToNumber(String)
+// Boolean R Number <=> ToNumber(Boolean) R Number
+
+// The entries in the first halves of xs, ys, and zs should be loose-equal to each other.
+
+var xs = [
+ // ToNumber(bool) == 0
+ false, false, false, false,
+
+ // ToNumber(bool) == 1
+ true, true, true, true,
+
+ // Filler
+ false, false, false, false,
+ true, true, true, true,
+];
+
+var ys = [
+ // ToNumber(str) == 0
+ "", "0", "0.0", ".0",
+
+ // ToNumber(str) == 1
+ "1", "1.0", "0x1", " 1\t\r\n",
+
+ // ToNumber(str) != {0, 1}
+ // (Duplicate entries to ensure they're neither equal to |true| nor to |false|.)
+ "not-a-number", "NaN", "Infinity", "2",
+ "not-a-number", "NaN", "Infinity", "2",
+];
+
+function Double(x) {
+ // numberToDouble always returns a Double valued number.
+ return numberToDouble(x);
+}
+
+var zs = [
+ // = 0
+ Double(0), Double(0), -0, -0,
+
+ // = 1
+ Double(1), Double(1), Double(1), Double(1),
+
+ // != {0, 1}
+ // (Duplicate entries to ensure they're neither equal to |true| nor to |false|.)
+ NaN, Infinity, Double(2), Double(-1.5),
+ NaN, Infinity, Double(2), Double(-1.5),
+];
+
+function testLooseEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ var r = j < (xs.length >> 1);
+
+ assertEq(x == y, r);
+ assertEq(y == x, r);
+
+ assertEq(x == z, r);
+ assertEq(z == x, r);
+ }
+}
+testLooseEqual();
+
+function testLooseNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ var r = j < (xs.length >> 1);
+
+ assertEq(x != y, !r);
+ assertEq(y != x, !r);
+
+ assertEq(x != z, !r);
+ assertEq(z != x, !r);
+ }
+}
+testLooseNotEqual();
+
+function testLessThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x < y, +x < +y);
+ assertEq(y < x, +y < +x);
+
+ assertEq(x < z, +x < +z);
+ assertEq(z < x, +z < +x);
+ }
+}
+testLessThan();
+
+function testLessThanEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x <= y, +x <= +y);
+ assertEq(y <= x, +y <= +x);
+
+ assertEq(x <= z, +x <= +z);
+ assertEq(z <= x, +z <= +x);
+ }
+}
+testLessThanEqual();
+
+function testGreaterThan() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x > y, +x > +y);
+ assertEq(y > x, +y > +x);
+
+ assertEq(x > z, +x > +z);
+ assertEq(z > x, +z > +x);
+ }
+}
+testGreaterThan();
+
+function testGreaterThanEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+ var z = zs[j];
+
+ assertEq(x >= y, +x >= +y);
+ assertEq(y >= x, +y >= +x);
+
+ assertEq(x >= z, +x >= +z);
+ assertEq(z >= x, +z >= +x);
+ }
+}
+testGreaterThanEqual();
diff --git a/js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js b/js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js
new file mode 100644
index 0000000000..321fbb47f6
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bound-construct-derived-class-ctor.js
@@ -0,0 +1,46 @@
+class Base {
+ constructor(b) {
+ this.b = b;
+ }
+}
+class Derived extends Base {
+ constructor(a, b) {
+ super(b);
+ this.a = a;
+ }
+}
+
+function testSimple() {
+ var boundCtor = Derived.bind(null, 1);
+ for (var i = 0; i < 100; i++) {
+ var o = new boundCtor(2);
+ assertEq(o.a, 1);
+ assertEq(o.b, 2);
+
+ }
+}
+testSimple();
+
+function testMegamorphic() {
+ var ctors = [
+ function(a, b) { this.a = a; this.b = b; this.c = 1; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 2; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 3; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 4; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 5; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 6; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 7; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 8; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 9; }.bind(null, 1),
+ function(a, b) { this.a = a; this.b = b; this.c = 10; }.bind(null, 1),
+ Derived.bind(null, 1),
+ Derived.bind(null, 1),
+ ];
+ for (var i = 0; i < 100; i++) {
+ var ctor = ctors[i % ctors.length];
+ var o = new ctor(2);
+ assertEq(o.a, 1);
+ assertEq(o.b, 2);
+ }
+}
+testMegamorphic();
diff --git a/js/src/jit-test/tests/cacheir/bound-construct-hook.js b/js/src/jit-test/tests/cacheir/bound-construct-hook.js
new file mode 100644
index 0000000000..6d31db4192
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bound-construct-hook.js
@@ -0,0 +1,18 @@
+function test() {
+ // Some bound callables that we're unlikely to optimize better in CacheIR.
+ var boundCtor = (new Proxy(Array, {})).bind(null, 1, 2, 3);
+ var boundNonCtor = (new Proxy(x => x + 1, {})).bind(null, 1, 2, 3);
+
+ for (var i = 0; i < 60; i++) {
+ var fun = i < 40 ? boundCtor : boundNonCtor;
+ var ex = null;
+ try {
+ var res = new fun(100, 101);
+ assertEq(JSON.stringify(res), "[1,2,3,100,101]");
+ } catch (e) {
+ ex = e;
+ }
+ assertEq(ex === null, i < 40);
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/cacheir/bound-construct-scripted.js b/js/src/jit-test/tests/cacheir/bound-construct-scripted.js
new file mode 100644
index 0000000000..056a538159
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bound-construct-scripted.js
@@ -0,0 +1,72 @@
+function testNewTargetGuard() {
+ var weirdNewTarget = function() {};
+ var fun = function() { return new.target; };
+ var boundFun = fun.bind(null);
+ for (var i = 0; i < 60; i++) {
+ var newTarget = i < 40 ? boundFun : weirdNewTarget;
+ var res = Reflect.construct(boundFun, [], newTarget);
+ assertEq(res, i < 40 ? fun : weirdNewTarget);
+ }
+}
+testNewTargetGuard();
+
+function testPrototypeGuard() {
+ var fun = function() {};
+ var boundFun = fun.bind(null);
+ var customPrototype1 = {};
+ var customPrototype2 = {};
+ fun.prototype = customPrototype1;
+
+ for (var i = 0; i < 60; i++) {
+ if (i === 40) {
+ fun.prototype = customPrototype2;
+ }
+ var res = new boundFun();
+ assertEq(Object.getPrototypeOf(res), i < 40 ? customPrototype1 : customPrototype2);
+ }
+}
+testPrototypeGuard();
+
+function testNonObjectPrototypeGuard() {
+ var fun = function() {};
+ var boundFun = fun.bind(null);
+ fun.prototype = null;
+ var customPrototype = {};
+
+ for (var i = 0; i < 60; i++) {
+ if (i === 40) {
+ fun.prototype = customPrototype;
+ }
+ var res = new boundFun();
+ assertEq(Object.getPrototypeOf(res), i < 40 ? Object.prototype : customPrototype);
+ }
+}
+testNonObjectPrototypeGuard();
+
+function testObjectReturnValue() {
+ var fun = function() { return Math; };
+ var boundFun = fun.bind(null);
+ for (var i = 0; i < 60; i++) {
+ var res = new boundFun();
+ assertEq(res, Math);
+ }
+}
+testObjectReturnValue();
+
+function testManyArgs() {
+ var fun = function(a, b, c, d, e, f, g, h, i, j) {
+ this.values = [a, b, c, d, e, f, g, h, i, j].join(",");
+ };
+ var boundFun1 = fun.bind(null, 1, 2);
+ var boundFun2 = fun.bind(null, 1, 2, 3, 4, 5, 6);
+ for (var i = 0; i < 60; i++) {
+ assertEq(new boundFun1().values, "1,2,,,,,,,,");
+ assertEq(new boundFun1(10, 11, 12, 13, 14).values, "1,2,10,11,12,13,14,,,");
+ assertEq(new boundFun1(10, 11, 12, 13, 14, 15, 16, 17).values, "1,2,10,11,12,13,14,15,16,17");
+
+ assertEq(new boundFun2().values, "1,2,3,4,5,6,,,,");
+ assertEq(new boundFun2(10, 11).values, "1,2,3,4,5,6,10,11,,");
+ assertEq(new boundFun2(10, 11, 12, 13, 14, 15, 16, 17).values, "1,2,3,4,5,6,10,11,12,13");
+ }
+}
+testManyArgs();
diff --git a/js/src/jit-test/tests/cacheir/bug1345707.js b/js/src/jit-test/tests/cacheir/bug1345707.js
new file mode 100644
index 0000000000..616a5e365e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1345707.js
@@ -0,0 +1,7 @@
+for (var i=0; i<10; i++) {
+ var o = {};
+ if (i & 1)
+ Object.preventExtensions(o);
+ o[0] = i;
+ assertEq(0 in o, !(i & 1));
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1357024.js b/js/src/jit-test/tests/cacheir/bug1357024.js
new file mode 100644
index 0000000000..a70f605378
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1357024.js
@@ -0,0 +1,12 @@
+function f() {
+ var o = {};
+ for (var j = 0; j < 15; j++) {
+ try {
+ o.__proto__ = o || j;
+ } catch(e) {
+ continue;
+ }
+ throw "Fail";
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/cacheir/bug1397026.js b/js/src/jit-test/tests/cacheir/bug1397026.js
new file mode 100644
index 0000000000..8e2dc00339
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1397026.js
@@ -0,0 +1,43 @@
+function f1() {
+ var o = {};
+ var values = [];
+ for (var i = 0; i < 6; ++i) {
+ var desc = {
+ value: i,
+ writable: true,
+ configurable: true,
+ enumerable: true
+ };
+ try {
+ Object.defineProperty(o, "p", desc);
+ } catch (e) {
+ }
+ if (i === 1) {
+ Object.defineProperty(o, "p", {configurable: false});
+ }
+ values.push(o.p);
+ }
+ assertEq(values.toString(), "0,1,1,1,1,1");
+}
+f1();
+
+function f2() {
+ var o = {};
+ for (var i = 0; i < 6; ++i) {
+ var desc = {
+ value: i,
+ writable: true,
+ configurable: true,
+ enumerable: true
+ };
+ try {
+ Object.defineProperty(o, "p", desc);
+ } catch (e) {
+ }
+ assertEq(Object.getOwnPropertyDescriptor(o, "p").enumerable, true);
+ if (i > 0) {
+ Object.defineProperty(o, "p", {enumerable: false});
+ }
+ }
+}
+f2();
diff --git a/js/src/jit-test/tests/cacheir/bug1414849.js b/js/src/jit-test/tests/cacheir/bug1414849.js
new file mode 100644
index 0000000000..1ebe86a82a
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1414849.js
@@ -0,0 +1,12 @@
+function g(s) {
+ for (var i = s; ; i--) {
+ if (s[i] !== ' ')
+ return;
+ }
+}
+function f() {
+ var s = "foo".match(/(.*)$/)[0];
+ return g(s);
+}
+for (var i = 0; i < 10; i++)
+ f();
diff --git a/js/src/jit-test/tests/cacheir/bug1420910.js b/js/src/jit-test/tests/cacheir/bug1420910.js
new file mode 100644
index 0000000000..41d17376dd
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1420910.js
@@ -0,0 +1,35 @@
+// Testing InstanceOf IC.
+
+Array.prototype.sum = function() {
+ return this.reduce(( acc, cur ) => acc + cur, 0);
+}
+
+
+Iters = 20;
+
+function resultArray(fn, obj) {
+ res = new Array();
+ for (var x = 0; x < Iters; x++) {
+ res.push(fn(obj) ? 1 : 0);
+ }
+ return res;
+}
+
+// Ensure alteration of .prototype invalidates IC
+function basic() {};
+
+protoA = { prop1: "1"};
+basic.prototype = protoA;
+
+io1 = x => { return x instanceof basic; }
+
+var x = new basic();
+beforePrototypeModification = resultArray(io1,x).sum(); //Attach and test IC
+assertEq(beforePrototypeModification,Iters);
+
+basic.prototype = {}; // Invalidate IC
+afterPrototypeModification = resultArray(io1,x).sum(); //Test
+assertEq(afterPrototypeModification,0);
+
+//Primitive LHS returns false.
+assertEq(resultArray(io1,0).sum(),0); \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/bug1423139.js b/js/src/jit-test/tests/cacheir/bug1423139.js
new file mode 100644
index 0000000000..31ad297a49
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1423139.js
@@ -0,0 +1,15 @@
+var c = 0;
+for (var i = 0; i < 5; i++) {
+ try {
+ Object.defineProperty([], "length", {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 0
+ });
+ } catch (e) {
+ assertEq(e instanceof TypeError, true);
+ c++;
+ }
+}
+assertEq(c, i);
diff --git a/js/src/jit-test/tests/cacheir/bug1438727.1.js b/js/src/jit-test/tests/cacheir/bug1438727.1.js
new file mode 100644
index 0000000000..683fd8ab90
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1438727.1.js
@@ -0,0 +1,22 @@
+let count = 0;
+function testMathyFunction(f, inputs) {
+ for (var j = 0; j < inputs.length; ++j)
+ for (var k = 0; k < inputs.length; ++k) {
+ let a = f(inputs[j], inputs[k]);
+ count += 1;
+ print("Number " + count + ": " + a + " (inputs "+inputs[j]+","+inputs[k]+")");
+ }
+}
+mathy0 = function(x, y) {
+ return (x / (y ? x : -Number.MAX_SAFE_INTEGER) > Math.fround(+y & +0x100000000) ** Math.fround(y))
+}
+testMathyFunction(mathy0, /*MARR*/ [
+ [1], , , [1],
+ [1], , [1], , , [1], , [1], , , [1],
+ [1], , [1], , , [1],
+ [1], , [1], , [1],
+ [1],
+ [1], , -1 / 0
+])
+
+testMathyFunction(mathy0, [, -Number.MAX_VALUE, Number.MIN_SAFE_INTEGER, 0x100000001, 0x07fffffff, -0x07fffffff, 0 / 0, Number.MIN_VALUE, -0x0ffffffff, Number.MAX_SAFE_INTEGER, 0x0ffffffff, -0x100000000, , 1 / 0, 0x080000000, -1 / 0, 0x100000000]) \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/bug1438727.2.js b/js/src/jit-test/tests/cacheir/bug1438727.2.js
new file mode 100644
index 0000000000..d6941f7ea9
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1438727.2.js
@@ -0,0 +1,6 @@
+var lfRandom = 0;
+var i = 0;
+while (i < 1000) {
+ lfRunTypeId = 4 + (lfRandom % 2);
+ i++;
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1438727.3.js b/js/src/jit-test/tests/cacheir/bug1438727.3.js
new file mode 100644
index 0000000000..0cdc9fab7c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1438727.3.js
@@ -0,0 +1,16 @@
+let lfPreamble = `
+assertEq = function(a,b) {
+ try { print(a); print(b); } catch(exc) {}
+}
+`;
+evaluate(lfPreamble, {});
+var a = [1, 2, 3];
+var b = [4, 5, 6];
+function testFold() {
+ for (var i = 0; i < 10; i++) {
+ var x = a[i];
+ var z = b[i];
+ if (((x / x) | 0) < 3) assertEq(x !== z, true);
+ }
+}
+for (var i = 0; i < 10; i++) testFold();
diff --git a/js/src/jit-test/tests/cacheir/bug1438727.4.js b/js/src/jit-test/tests/cacheir/bug1438727.4.js
new file mode 100644
index 0000000000..85a9f54a84
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1438727.4.js
@@ -0,0 +1,4 @@
+function f(v, e) {
+ for (var i = 2; i < 9; i++) v %= e;
+}
+f(0, 1);
diff --git a/js/src/jit-test/tests/cacheir/bug1438727.js b/js/src/jit-test/tests/cacheir/bug1438727.js
new file mode 100644
index 0000000000..4846195da8
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1438727.js
@@ -0,0 +1,7 @@
+const handler = {}
+function testArray(arr) {
+ let proxy = new Proxy(arr, handler)
+ proxy.sort((x, y) => 1 * x - y);
+ arr.sort((x, y) => 1 * x - y);
+}
+testArray([5, (this), 2, 99]); \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/bug1439180.js b/js/src/jit-test/tests/cacheir/bug1439180.js
new file mode 100644
index 0000000000..90d77f3204
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1439180.js
@@ -0,0 +1,9 @@
+h = function f(x) {
+ x = +"NaN";
+ return /I/ (~x);
+ }
+for (var j = 0; j < 3; j++) {
+ try {
+ h();
+ } catch (e) {}
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1448136.js b/js/src/jit-test/tests/cacheir/bug1448136.js
new file mode 100644
index 0000000000..e08e67d80d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1448136.js
@@ -0,0 +1,23 @@
+print = function(s) { return s.toString(); }
+assertEq = function(a,b) {
+ try { print(a); print(b); } catch(exc) {}
+}
+g = newGlobal({newCompartment: true});
+g.parent = this;
+g.eval("(" + function() {
+ Debugger(parent).onExceptionUnwind = function(frame) {
+ frame.older
+ }
+} + ")()")
+function a() {};
+function b() {};
+for (let _ of Array(100))
+ assertEq(b instanceof a, true);
+function c(){};
+function d(){};
+function e(){};
+Object.defineProperty(a, Symbol.hasInstance, {value: assertEq });
+let funcs = [a, b, c, d];
+for (let f of funcs)
+ assertEq(e instanceof f, true);
+
diff --git a/js/src/jit-test/tests/cacheir/bug1451976.js b/js/src/jit-test/tests/cacheir/bug1451976.js
new file mode 100644
index 0000000000..565362bc37
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1451976.js
@@ -0,0 +1,11 @@
+// This test case originally failed under --enable-more-deterministic
+
+setJitCompilerOption("ion.forceinlineCaches", 1);
+function f(y, z) {
+ return Math.fround(z) < ~y;
+};
+var x = [2 ** 53 - 2, 0];
+for (var i = 0; i < 3; ++i) {
+ assertEq(f(x[0], x[1]),true);
+}
+
diff --git a/js/src/jit-test/tests/cacheir/bug1451984.js b/js/src/jit-test/tests/cacheir/bug1451984.js
new file mode 100644
index 0000000000..bfbcb109ba
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1451984.js
@@ -0,0 +1,10 @@
+// This test case originally failed only with --enable-more-deterministic
+
+setJitCompilerOption("ion.forceinlineCaches", 1);
+function f(x) {
+ return Math.pow(Math.fround(Math.fround()), ~(x >>> 0));
+}
+assertEq(f(-1),1);
+assertEq(f(-1),1);
+assertEq(f(-1),1);
+assertEq(f(-1),1);
diff --git a/js/src/jit-test/tests/cacheir/bug1459754.js b/js/src/jit-test/tests/cacheir/bug1459754.js
new file mode 100644
index 0000000000..bfbe626acc
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1459754.js
@@ -0,0 +1,9 @@
+function f(x) {
+ this["__proto__"] = x;
+ let tmp = this.toString;
+ assertEq(x === null, tmp === void 0);
+}
+
+for (let e of [[], null, []]) {
+ new f(e);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1462280.js b/js/src/jit-test/tests/cacheir/bug1462280.js
new file mode 100644
index 0000000000..d4581ad6ac
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1462280.js
@@ -0,0 +1,3 @@
+for (var i = 0; i < 2; i++) {
+ evaluate("var obj = {[Symbol.iterator]: Symbol.iterator};");
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1471361.js b/js/src/jit-test/tests/cacheir/bug1471361.js
new file mode 100644
index 0000000000..d50acdf558
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1471361.js
@@ -0,0 +1,13 @@
+// This is a fuzzer-found crash inducing test.
+// Pass is simply not crashing.
+(function () {
+ f = function(y) {
+ ~+y;
+ }
+ x = [new Number];
+ for (var j = 0; j < 3; ++j) {
+ for (var k = 0; k < 3; ++k) {
+ f(x[k]);
+ }
+ }
+})();
diff --git a/js/src/jit-test/tests/cacheir/bug1483183.js b/js/src/jit-test/tests/cacheir/bug1483183.js
new file mode 100644
index 0000000000..ec95b52a9f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1483183.js
@@ -0,0 +1,8 @@
+if (!('stackTest' in this))
+ quit();
+
+stackTest(new Function(`
+newGlobal({
+ sameZoneAs: []
+}).frame;
+`));
diff --git a/js/src/jit-test/tests/cacheir/bug1488786-2.js b/js/src/jit-test/tests/cacheir/bug1488786-2.js
new file mode 100644
index 0000000000..7ac42f2ce4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1488786-2.js
@@ -0,0 +1,27 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function protoChange() {
+ var o = {0: 0, 1: 0, 0x10000: 0, 0x20000: 0};
+
+ var tests = [1, 0, 0x10000, 0x20000];
+
+ function result_map(key, i) {
+ if (i > 5 && key == 0x20000)
+ return undefined;
+ return 0;
+ }
+
+ for (var i = 0; i < 10; i++) {
+ for (var key of tests) {
+ assertEq(o[key], result_map(key, i));
+ }
+
+ if (i == 5) {
+ delete o[0x20000];
+ }
+ }
+}
+
+for (var i = 0; i < 10; i++) {
+ protoChange();
+} \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/bug1488786.js b/js/src/jit-test/tests/cacheir/bug1488786.js
new file mode 100644
index 0000000000..656f1f0d2e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1488786.js
@@ -0,0 +1,38 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+var A = Array(2**18);
+A[0] = "A";
+A[1] = "B";
+A[2**14] = "C";
+A[2**31-1] = "D";
+A[-1] = "E";
+
+function get_thee(a,x) {
+ return a[x];
+}
+
+// Warmup IC
+for (var i = 0; i < 30; i++) {
+ get_thee(A, i % A.length);
+}
+
+// numberToDouble always returns a double-typed Number, so helps
+// us ensure we're accessing the array with a double-typed Number index.
+var y = numberToDouble(1);
+var z = 2**31-1;
+// Ensure we handle negative indices.
+var a = -1;
+
+function test() {
+ for (var i = 0; i < 30; i++) {
+ assertEq(get_thee(A,y), "B");
+ assertEq(get_thee(A,z), "D");
+ assertEq(get_thee(A,a), "E");
+ }
+}
+test();
+
+if (!('oomTest' in this))
+ quit();
+
+oomTest(test);
diff --git a/js/src/jit-test/tests/cacheir/bug1494537-plain.js b/js/src/jit-test/tests/cacheir/bug1494537-plain.js
new file mode 100644
index 0000000000..e17cd3bd1d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1494537-plain.js
@@ -0,0 +1,123 @@
+// Version of bug1494537.js for plain objects.
+
+let offsets = [213, 559, 255, 515, 30, 507, 252, 329, 487, 7];
+
+function update_index(i, j) {
+ var offset = offsets[j % offsets.length];
+ return i + offset;
+}
+
+function compute_index(initial, count) {
+ for (var i = 0; i < count; i++) {
+ initial = update_index(initial, i);
+ }
+ return initial;
+}
+
+// This is written so that the IC added in the bug activates.
+function mutate_object(obj, count, epsilon = 0) {
+ var index = 0;
+ for (var i = 0; i < count; i++) {
+ index = update_index(index, i);
+ obj[index] = i + epsilon;
+ }
+ return obj[offsets[0]+offsets[1]] === (1 + epsilon) &&
+ obj[10] === undefined;
+}
+
+// Monomorphizing mutate_object to ensure we get the IC chains we want
+function create_variant(variant) {
+ var source = mutate_object.toString().replace("mutate_object", "mutate_object_"+variant);
+ return source;
+}
+
+function test_basic() {
+ eval(create_variant("basic"));
+ var x = {};
+
+ var count = 100;
+ assertEq(mutate_object_basic(x, count), true);
+ var end = compute_index(0, count);
+ assertEq(x[end], count - 1);
+ assertEq(x[end - 1], undefined);
+}
+
+// Ensure the IC respects frozen.
+function test_frozen() {
+ eval(create_variant("frozen"));
+ var x = {};
+ Object.freeze(x);
+
+ var count = 100;
+ assertEq(mutate_object_frozen(x, count), false);
+
+ var end = compute_index(0, count);
+
+ var y = {};
+ assertEq(mutate_object_frozen(y, count), true);
+ assertEq(y[end], count - 1);
+ Object.freeze(y);
+
+ // After a mutated object is frozen, can't subsequently modify elements
+ assertEq(mutate_object_frozen(x, count, 10), false);
+ assertEq(y[end], count - 1);
+}
+
+// Let's make sure updates to the object happen as expected.
+function test_update() {
+ eval(create_variant("update"));
+
+ var x = {};
+ var count = 100;
+ assertEq(mutate_object_update(x, count), true);
+ var end = compute_index(0, count);
+ assertEq(x[end], count - 1);
+ assertEq(x[end - 1], undefined);
+
+ var epsilon = 2;
+ mutate_object_update(x, 200, epsilon);
+ assertEq(x[end], count -1 + epsilon)
+}
+
+// Elements may be non-writable, let us not write them.
+function test_nonwritable() {
+ eval(create_variant("nonwritable"));
+ var x = {};
+ var count = 100;
+ var index = compute_index(0, 10);
+ Object.defineProperty(x, index, {value: -10, writable: false});
+ mutate_object_nonwritable(x, count);
+ assertEq(x[index], -10);
+}
+
+// Random indices can get setters, let's make sure we honour those.
+function test_setter() {
+ eval(create_variant("setter"));
+ var x = {};
+ var count = 100;
+ var index = compute_index(0, 80);
+ var sigil = 0;
+ Object.defineProperty(x, index, {set(newVal) {sigil++; }});
+ mutate_object_setter(x, count);
+ assertEq(sigil, 1);
+ assertEq(x[index], undefined);
+}
+
+// Ensure indexes on the prototype don't break things.
+function test_proto_indices() {
+ eval(create_variant("proto_indices"));
+ var x = {};
+ var count = 100;
+ var index = compute_index(0, 80);
+ x.__proto__[index] = "hello";
+ mutate_object_proto_indices(x, count);
+ assertEq(x.__proto__[index], "hello");
+ assertEq(x[index], 79);
+}
+
+test_basic();
+test_frozen();
+test_update();
+test_nonwritable();
+test_setter();
+test_proto_indices();
diff --git a/js/src/jit-test/tests/cacheir/bug1494537.js b/js/src/jit-test/tests/cacheir/bug1494537.js
new file mode 100644
index 0000000000..7bf139819c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1494537.js
@@ -0,0 +1,125 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+let offsets = [213, 559, 255, 515, 30, 507, 252, 329, 487, 7];
+
+function update_index(i, j) {
+ var offset = offsets[j % offsets.length];
+ return i + offset;
+}
+
+function compute_index(initial, count) {
+ for (var i = 0; i < count; i++) {
+ initial = update_index(initial, i);
+ }
+ return initial;
+}
+
+// This is written so that the IC added in the bug activates.
+function mutate_array(array, count, epsilon = 0) {
+ var index = 0;
+ for (var i = 0; i < count; i++) {
+ index = update_index(index, i);
+ array[index] = i + epsilon;
+ }
+ return array[offsets[0]+offsets[1]] === (1 + epsilon) &&
+ array[10] === undefined;
+}
+
+// Monomorphizing mutate_array to ensure we get the IC chains we want
+function create_variant(variant) {
+ var source = mutate_array.toString().replace("mutate_array", "mutate_array_"+variant);
+ return source;
+}
+
+function test_basic() {
+ eval(create_variant("basic"));
+ var x = [];
+
+ var count = 100;
+ assertEq(mutate_array_basic(x, count), true);
+ var end = compute_index(0, count);
+ assertEq(x[end], count - 1);
+ assertEq(x[end - 1], undefined);
+}
+
+// Ensure the IC respects frozen.
+function test_frozen() {
+ eval(create_variant("frozen"));
+ var x = [];
+ Object.freeze(x);
+
+ var count = 100;
+ assertEq(mutate_array_frozen(x, count), false);
+ assertEq(x.length, 0);
+
+ var end = compute_index(0, count);
+
+ var y = [];
+ assertEq(mutate_array_frozen(y, count), true);
+ assertEq(y[end], count - 1);
+ Object.freeze(y);
+
+ // After a mutated array is frozen, can't subsequently modify elements
+ assertEq(mutate_array_frozen(x, count, 10), false);
+ assertEq(y[end], count - 1);
+}
+
+// Let's make sure updates to the array happen as expected.
+function test_update() {
+ eval(create_variant("update"));
+
+ var x = [];
+ var count = 100;
+ assertEq(mutate_array_update(x, count), true);
+ var end = compute_index(0, count);
+ assertEq(x[end], count - 1);
+ assertEq(x[end - 1], undefined);
+
+ var epsilon = 2;
+ mutate_array_update(x, 200, epsilon);
+ assertEq(x[end], count -1 + epsilon)
+}
+
+// Elements may be non-writable, let us not write them.
+function test_nonwritable() {
+ eval(create_variant("nonwritable"));
+ var x = [];
+ var count = 100;
+ var index = compute_index(0, 10);
+ Object.defineProperty(x, index, {value: -10, writable: false});
+ mutate_array_nonwritable(x, count);
+ assertEq(x[index], -10);
+}
+
+// Random indices can get setters, let's make sure we honour those.
+function test_setter() {
+ eval(create_variant("setter"));
+ var x = [];
+ var count = 100;
+ var index = compute_index(0, 80);
+ var sigil = 0;
+ Object.defineProperty(x, index, {set(newVal) {sigil++; }});
+ mutate_array_setter(x, count);
+ assertEq(sigil, 1);
+ assertEq(x[index], undefined);
+}
+
+// Ensure indexes on the prototype don't break things;
+//
+function test_proto_indices() {
+ eval(create_variant("proto_indices"));
+ var x = [];
+ var count = 100;
+ var index = compute_index(0, 80);
+ x.__proto__[index] = "hello";
+ mutate_array_proto_indices(x, count);
+ assertEq(x.__proto__[index], "hello");
+ assertEq(x[index], 79);
+}
+
+test_basic();
+test_frozen();
+test_update();
+test_nonwritable();
+test_setter();
+test_proto_indices();
diff --git a/js/src/jit-test/tests/cacheir/bug1500255.js b/js/src/jit-test/tests/cacheir/bug1500255.js
new file mode 100644
index 0000000000..41b83fc02b
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1500255.js
@@ -0,0 +1,11 @@
+
+setJitCompilerOption("offthread-compilation.enable", 0);
+setJitCompilerOption("baseline.warmup.trigger", 0);
+setJitCompilerOption("ion.warmup.trigger", 0);
+
+foo();
+
+function foo() {
+ Array.prototype.__proto__ = null;
+ Array.prototype[1] = 'bar';
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1502143.js b/js/src/jit-test/tests/cacheir/bug1502143.js
new file mode 100644
index 0000000000..2cc7bc1f55
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1502143.js
@@ -0,0 +1,15 @@
+
+setJitCompilerOption("baseline.warmup.trigger", 10);
+setJitCompilerOption("ion.warmup.trigger", 100);
+
+main();
+
+function main() {
+ new Uint8Array(1024).buffer;
+ var b = [];
+ b[10000] = 1;
+ [].concat(b)
+ var a2 = new Int32Array(2);
+ b.__proto__ = a2;
+ [].concat(b)
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1502709.js b/js/src/jit-test/tests/cacheir/bug1502709.js
new file mode 100644
index 0000000000..535443050d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1502709.js
@@ -0,0 +1,17 @@
+
+setJitCompilerOption("baseline.warmup.trigger", 0);
+main();
+
+function main() {
+ try {
+ x = [];
+ Object.defineProperty(x, 1, {});
+ y = [];
+ Object.defineProperty(y, 1, {});
+ y.__proto__ = null;
+ Array.prototype.sort.apply(x, [function() {}]);
+ } catch (e) {}
+ try {
+ Array.prototype.sort.apply(y, [function() {}]);
+ } catch (e) {}
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1509293.js b/js/src/jit-test/tests/cacheir/bug1509293.js
new file mode 100644
index 0000000000..34306bc223
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1509293.js
@@ -0,0 +1,2 @@
+var summary = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+Array.prototype.push([...summary]);
diff --git a/js/src/jit-test/tests/cacheir/bug1514682.js b/js/src/jit-test/tests/cacheir/bug1514682.js
new file mode 100644
index 0000000000..c509700b4e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1514682.js
@@ -0,0 +1,15 @@
+function f(o, n) {
+ if (n) {
+ o[n] = true;
+ } else {
+ o.x = true;
+ }
+}
+
+// Warm up object so HadElementsAccess check will trip next
+var o = {};
+for (var i = 0; i < 43; ++i) {
+ o["x" + i] = true;
+}
+
+f(o, "y");
diff --git a/js/src/jit-test/tests/cacheir/bug1526872.js b/js/src/jit-test/tests/cacheir/bug1526872.js
new file mode 100644
index 0000000000..33d65e9443
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1526872.js
@@ -0,0 +1,12 @@
+
+setJitCompilerOption("offthread-compilation.enable", 0);
+setJitCompilerOption("baseline.warmup.trigger", 0);
+setJitCompilerOption("ion.warmup.trigger", 0);
+
+for (var i = 0; i < 5; i++) {
+ assertEq(foo(1n), false);
+}
+
+function foo(x) {
+ return x == null || x == undefined;
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1536228.js b/js/src/jit-test/tests/cacheir/bug1536228.js
new file mode 100644
index 0000000000..2a094eb377
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1536228.js
@@ -0,0 +1,40 @@
+load(libdir + "asserts.js");
+
+function test() {
+ var plainDataProperty = {
+ value: 0, enumerable: true, configurable: true, writable: true
+ };
+ var nonEnumerableProperty = {
+ value: 0, enumerable: false, configurable: true, writable: true
+ };
+ var nonConfigurableProperty = {
+ value: 0, enumerable: true, configurable: false, writable: true
+ };
+ var n = 5000;
+ for (var i = 0; i < n; ++i) {
+ var obj = {};
+
+ // Create a different shape for each object to ensure JSOP_INITELEM
+ // below can get into the megamorphic state.
+ Object.defineProperty(obj, "bar" + i, nonEnumerableProperty);
+
+ // Plain data property, will be created through DefineDataProperty,
+ // which is emitted as JSOP_INITELEM. The object doesn't have a "foo"
+ // property, so JSOP_INITELEM can simply create a new property.
+ Object.defineProperty(obj, "foo", plainDataProperty);
+
+ // Redefine as a non-data property for the last object.
+ var desc = (i + 1 !== n) ? plainDataProperty : nonConfigurableProperty;
+ Object.defineProperty(obj, "foo", desc);
+
+ // Again JSOP_INITELEM, but this time JSOP_INITELEM can't simply add a
+ // property, because "foo" is already present. And for the last object,
+ // which has a non-configurable "foo" property, this call must throw a
+ // TypeError exception.
+ Object.defineProperty(obj, "foo", plainDataProperty);
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ assertThrowsInstanceOf(test, TypeError);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1612636.js b/js/src/jit-test/tests/cacheir/bug1612636.js
new file mode 100644
index 0000000000..710889b308
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1612636.js
@@ -0,0 +1,10 @@
+var concat = String.prototype.concat;
+function f18(b83) {
+ var a11 = arguments;
+ if (b83)
+ f18(false);
+ g22 = {};
+ g22.apply = concat;
+ g22.apply(null, a11);
+}
+f18(true);
diff --git a/js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js b/js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js
new file mode 100644
index 0000000000..a64e2aabb0
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1651732-ionic-getprop-super.js
@@ -0,0 +1,28 @@
+class Base {
+ static a = 0;
+ static [Symbol.iterator] = 0;
+}
+
+class Derived extends Base {
+ static m(key) {
+ // Attach an IC through IonGetPropSuperIC.
+ return super[key];
+ }
+}
+
+var key = {
+ value: "a",
+
+ [Symbol.toPrimitive]() {
+ return this.value;
+ }
+};
+
+for (var i = 0; i < 100; ++i) {
+ // Change key[Symbol.toPrimitive] to return a symbol after some warm-up.
+ if (i > 80) {
+ key.value = Symbol.iterator;
+ }
+
+ assertEq(Derived.m(key), 0);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js
new file mode 100644
index 0000000000..9a4362f410
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-get.js
@@ -0,0 +1,23 @@
+// An object with a custom [Symbol.toPrimitive] function.
+var key = {
+ value: "a",
+
+ [Symbol.toPrimitive]() {
+ return this.value;
+ }
+};
+
+var target = {};
+var obj = new Proxy(target, {});
+
+for (var i = 0; i < 100; ++i) {
+ // Change key[Symbol.toPrimitive] to return a symbol after some warm-up.
+ if (i > 80) {
+ key.value = Symbol.iterator;
+ }
+
+ target[key.value] = i;
+
+ // Attach an IC for JSOp::GetElem on proxies.
+ assertEq(obj[key], i);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js
new file mode 100644
index 0000000000..004b5982d8
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-has.js
@@ -0,0 +1,24 @@
+// An object with a custom [Symbol.toPrimitive] function.
+var key = {
+ value: "a",
+
+ [Symbol.toPrimitive]() {
+ return this.value;
+ }
+};
+
+var target = {
+ a: 0,
+ [Symbol.iterator]: 0,
+};
+var obj = new Proxy(target, {});
+
+for (var i = 0; i < 100; ++i) {
+ // Change key[Symbol.toPrimitive] to return a symbol after some warm-up.
+ if (i > 80) {
+ key.value = Symbol.iterator;
+ }
+
+ // Attach an IC for JSOp::In on proxies.
+ assertEq(key in obj, true);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js
new file mode 100644
index 0000000000..90f94a4e48
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-hasOwn.js
@@ -0,0 +1,24 @@
+// An object with a custom [Symbol.toPrimitive] function.
+var key = {
+ value: "a",
+
+ [Symbol.toPrimitive]() {
+ return this.value;
+ }
+};
+
+var target = {
+ a: 0,
+ [Symbol.iterator]: 0,
+};
+var obj = new Proxy(target, {});
+
+for (var i = 0; i < 100; ++i) {
+ // Change key[Symbol.toPrimitive] to return a symbol after some warm-up.
+ if (i > 80) {
+ key.value = Symbol.iterator;
+ }
+
+ // Attach an IC for JSOp::HasOwn on proxies.
+ assertEq(Object.prototype.hasOwnProperty.call(obj, key), true);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js b/js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js
new file mode 100644
index 0000000000..a52362a91f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1651732-proxy-set.js
@@ -0,0 +1,23 @@
+// An object with a custom [Symbol.toPrimitive] function.
+var key = {
+ value: "a",
+
+ [Symbol.toPrimitive]() {
+ return this.value;
+ }
+};
+
+var target = {};
+var obj = new Proxy(target, {});
+
+for (var i = 0; i < 100; ++i) {
+ // Change key[Symbol.toPrimitive] to return a symbol after some warm-up.
+ if (i > 80) {
+ key.value = Symbol.iterator;
+ }
+
+ obj[key] = i;
+
+ // Attach an IC for JSOp::SetElem on proxies.
+ assertEq(target[key.value], i);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1654947.js b/js/src/jit-test/tests/cacheir/bug1654947.js
new file mode 100644
index 0000000000..673e5fd0c2
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1654947.js
@@ -0,0 +1,11 @@
+function f() {
+ let g = x => (x >>> {} >>> x, x);
+ let arr = [0, 0xffff, undefined];
+ for (let i = 0; i < 10; i++) {
+ for (let j = 0; j < 3; j++) {
+ let y = g(arr[j]);
+ assertEq(y, arr[j]);
+ }
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/cacheir/bug1685684.js b/js/src/jit-test/tests/cacheir/bug1685684.js
new file mode 100644
index 0000000000..06b37a852c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1685684.js
@@ -0,0 +1,8 @@
+let g = newGlobal({immutablePrototype: false});
+g.__proto__ = {};
+g.evaluate(`(function() {
+ for (var i = 0; i < 5; i++) {
+ d = eval('1');
+ assertEq(d, 1);
+ }
+})()`);
diff --git a/js/src/jit-test/tests/cacheir/bug1685925-1.js b/js/src/jit-test/tests/cacheir/bug1685925-1.js
new file mode 100644
index 0000000000..630b667026
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1685925-1.js
@@ -0,0 +1,15 @@
+// |jit-test| error: TypeError: get length method
+function f(o) {
+ return o.length;
+}
+let objects = [
+ {},
+ {length: 0},
+ [],
+ {x: 0, length: 0},
+ {x: 0, y: 0, length: 0},
+ {x: 0, y: 0, z: 0, length: 0},
+ new Uint32Array(),
+ Object.create(new Uint8Array()),
+];
+objects.forEach(f);
diff --git a/js/src/jit-test/tests/cacheir/bug1685925-2.js b/js/src/jit-test/tests/cacheir/bug1685925-2.js
new file mode 100644
index 0000000000..c009a1c0ec
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1685925-2.js
@@ -0,0 +1,7 @@
+// |jit-test| error: TypeError: get length method
+setJitCompilerOption("ic.force-megamorphic", 1);
+function f(o) {
+ return o.length;
+}
+f(new Int8Array(4));
+f(Object.create(new Uint8Array()));
diff --git a/js/src/jit-test/tests/cacheir/bug1713556.js b/js/src/jit-test/tests/cacheir/bug1713556.js
new file mode 100644
index 0000000000..0db7a3aa25
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1713556.js
@@ -0,0 +1,25 @@
+// |jit-test| skip-if: !wasmIsSupported()
+
+function wasmEvalText(str ) {
+ binary = wasmTextToBinary(str)
+ m = new WebAssembly.Module(binary)
+ return new WebAssembly.Instance(m)
+}
+ins = wasmEvalText(`
+ (func (export "get1") (param))
+`);
+ins.exports.get1(
+ 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+ ,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
+)
diff --git a/js/src/jit-test/tests/cacheir/bug1757634.js b/js/src/jit-test/tests/cacheir/bug1757634.js
new file mode 100644
index 0000000000..97ea3a87f4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1757634.js
@@ -0,0 +1,29 @@
+function testSmallIndex() {
+ var proto = Object.create(null);
+ var arr = [];
+ Object.setPrototypeOf(arr, proto);
+
+ proto[0] = 123;
+ Object.freeze(proto);
+
+ for (var i = 0; i < 20; i++) {
+ arr[0] = 321;
+ }
+ assertEq(arr[0], 123);
+}
+testSmallIndex();
+
+function testLargeIndex() {
+ var proto = Object.create(null);
+ var arr = [];
+ Object.setPrototypeOf(arr, proto);
+
+ proto[98765432] = 123;
+ Object.freeze(proto);
+
+ for (var i = 0; i < 20; i++) {
+ arr[98765432] = 321;
+ }
+ assertEq(arr[98765432], 123);
+}
+testLargeIndex();
diff --git a/js/src/jit-test/tests/cacheir/bug1772824.js b/js/src/jit-test/tests/cacheir/bug1772824.js
new file mode 100644
index 0000000000..f399a77cea
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1772824.js
@@ -0,0 +1,15 @@
+function foo() { eval(); boundName += 1; }
+
+boundName = 0;
+for (var i = 0; i < 10; i++) {
+ eval("var x" + i + " = 0;");
+ foo();
+}
+
+// Redefine variable as const
+evaluate(`
+ const boundName = 0;
+ for (var i = 0; i < 2; i++) {
+ try { foo(); } catch {}
+ }
+`);
diff --git a/js/src/jit-test/tests/cacheir/bug1785200.js b/js/src/jit-test/tests/cacheir/bug1785200.js
new file mode 100644
index 0000000000..d568f6108d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1785200.js
@@ -0,0 +1,15 @@
+const target = {};
+const receiver = {};
+const id = 5;
+
+let count = 0;
+function getter() {
+ count++;
+ assertEq(this, receiver);
+}
+Object.defineProperty(target, id, { get: getter });
+
+for (var i = 0; i < 100; i++) {
+ Reflect.get(target, id, receiver);
+}
+assertEq(count, 100);
diff --git a/js/src/jit-test/tests/cacheir/bug1788528-1.js b/js/src/jit-test/tests/cacheir/bug1788528-1.js
new file mode 100644
index 0000000000..038ef1e12e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1788528-1.js
@@ -0,0 +1,15 @@
+// |jit-test| error:ReferenceError: can't access lexical declaration
+function f(i) {
+ if (i === 19) {
+ g();
+ }
+ let val = 0;
+ function g() {
+ eval("");
+ val ||= 1;
+ }
+ g();
+}
+for (var i = 0; i < 20; i++) {
+ f(i);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1788528-2.js b/js/src/jit-test/tests/cacheir/bug1788528-2.js
new file mode 100644
index 0000000000..5f3701c3b0
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1788528-2.js
@@ -0,0 +1,15 @@
+// |jit-test| error:ReferenceError: can't access lexical declaration
+function f(i) {
+ if (i === 19) {
+ g();
+ }
+ let val = 0;
+ function g() {
+ eval("");
+ val = 1;
+ }
+ g();
+}
+for (var i = 0; i < 20; i++) {
+ f(i);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1788528-3.js b/js/src/jit-test/tests/cacheir/bug1788528-3.js
new file mode 100644
index 0000000000..52414669d4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1788528-3.js
@@ -0,0 +1,15 @@
+// |jit-test| error:ReferenceError: can't access lexical declaration
+function f(i) {
+ if (i === 19) {
+ g();
+ }
+ let val = 0;
+ function g() {
+ eval("");
+ val += 1;
+ }
+ g();
+}
+for (var i = 0; i < 20; i++) {
+ f(i);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1788528-4.js b/js/src/jit-test/tests/cacheir/bug1788528-4.js
new file mode 100644
index 0000000000..67ab57e433
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1788528-4.js
@@ -0,0 +1,15 @@
+// |jit-test| error:ReferenceError: can't access lexical declaration
+function f(i) {
+ if (i === 19) {
+ g();
+ }
+ let val = 0;
+ function g() {
+ eval("");
+ val;
+ }
+ g();
+}
+for (var i = 0; i < 20; i++) {
+ f(i);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1804634.js b/js/src/jit-test/tests/cacheir/bug1804634.js
new file mode 100644
index 0000000000..c34e815f6b
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1804634.js
@@ -0,0 +1,34 @@
+// |jit-test| --ion-instruction-reordering=off
+
+function inner(o) {
+ return o.x;
+}
+function outer(o) {
+ return inner(o);
+}
+
+with ({}) {}
+
+var arr = [];
+for (var i = 0; i < 3; i++) {
+ var obj = {x: 1};
+ obj["y" + i] = 2;
+ arr.push(obj);
+}
+
+// Transpile a GuardMultipleShapes with 3 shapes.
+for (var i = 0; i < 2000; i++) {
+ assertEq(outer(arr[i % arr.length]), 1);
+}
+
+// Bail out ten times with new shapes.
+for (var i = 0; i < 10; i++) {
+ var obj = {x: 1};
+ obj["z" + i] = 3;
+ assertEq(outer(obj), 1);
+}
+
+// Trigger a recompilation.
+for (var i = 0; i < 2000; i++) {
+ assertEq(outer(arr[i % arr.length]), 1);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1819486.js b/js/src/jit-test/tests/cacheir/bug1819486.js
new file mode 100644
index 0000000000..6bb3e799ea
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1819486.js
@@ -0,0 +1,18 @@
+class Base {}
+class Derived extends Base {
+ constructor(a) { super(a); this.a = a; }
+}
+function test() {
+ var boundCtor = Derived.bind();
+ for (var i = 0; i < 40; i++) {
+ new boundCtor();
+ var ex = null;
+ try {
+ boundCtor();
+ } catch(e) {
+ ex = e;
+ }
+ assertEq(ex instanceof TypeError, true);
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/cacheir/bug1823212.js b/js/src/jit-test/tests/cacheir/bug1823212.js
new file mode 100644
index 0000000000..951b3794cc
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1823212.js
@@ -0,0 +1,13 @@
+with (newGlobal(this)) {
+ eval(`
+ function bar() {}
+ function foo(a) {
+ try {
+ foo();
+ } catch {
+ bar(...arguments);
+ }
+ }
+ foo();
+ `);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1834038.js b/js/src/jit-test/tests/cacheir/bug1834038.js
new file mode 100644
index 0000000000..fb7581d3fc
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1834038.js
@@ -0,0 +1,9 @@
+function foo() {
+ const v4 = [];
+ v4[1024] = {};
+ v4.splice(0).push(0);
+}
+
+for (let v0 = 0; v0 < 1000; v0++) {
+ foo();
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1837157.js b/js/src/jit-test/tests/cacheir/bug1837157.js
new file mode 100644
index 0000000000..c468a1a259
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1837157.js
@@ -0,0 +1,36 @@
+// |jit-test| --fast-warmup; --no-threads
+with ({}) {}
+
+function foo(a) {
+ a.prop = 0;
+}
+
+class A {
+ set prop(x) { }
+}
+
+function newShape() {
+ class B extends A {}
+ return new B();
+}
+
+function triggerIonCompile() {
+ with ({}) {}
+ for (var i = 0; i < 50; i++) {
+ foo(initialShapes[i % initialShapes.length])
+ }
+}
+
+var initialShapes = [];
+for (var i = 0; i < 8; i++) {
+ initialShapes.push(newShape());
+}
+
+triggerIonCompile();
+
+for (var i = 0; i < 10; i++) {
+ foo(0);
+}
+foo(newShape());
+
+triggerIonCompile();
diff --git a/js/src/jit-test/tests/cacheir/bug1842617.js b/js/src/jit-test/tests/cacheir/bug1842617.js
new file mode 100644
index 0000000000..1524d5733f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1842617.js
@@ -0,0 +1,30 @@
+// |jit-test| --baseline-eager
+
+function foo(src) {
+ try {
+ evaluate(src);
+ } catch {}
+ Math = undefined;
+}
+
+foo(``);
+foo(``);
+foo(`var a = 0;`);
+foo(`var b = 0;`);
+foo(`var c = 0;`);
+foo(`var d = 0;`);
+foo(`undef();`);
+foo(`{`);
+foo(`
+ gc();
+ e = 0;
+ gc();
+`);
+foo(`
+ var f = 0;
+ gc();
+ delete Math;
+`);
+foo(`
+ g = undefined;
+`);
diff --git a/js/src/jit-test/tests/cacheir/bug1851599.js b/js/src/jit-test/tests/cacheir/bug1851599.js
new file mode 100644
index 0000000000..c960ee3f10
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1851599.js
@@ -0,0 +1,44 @@
+// |jit-test| --fast-warmup; --no-threads
+
+function foo(phase, o1, o2) {
+ switch (phase) {
+ case 1:
+ return o1.x;
+ case 2:
+ return o1.x + o2.x;
+ }
+}
+
+// Set `foo` as last child and `bar` as last parent.
+function phase1() {
+ eval(`
+ function bar(o) {
+ foo(1, o);
+ }
+ with ({}) {}
+ for (var j = 0; j < 100; j++) {
+ var obj = {x: 1};
+ obj["y" + (j % 10)] = 2;
+ bar(obj);
+ }
+ bar({y: 1, x: 2});
+ `);
+}
+phase1();
+
+// Collect `bar`.
+gc();
+
+// Recompile `foo` monomorphically.
+with ({}) {}
+for (var i = 0; i < 100; i++) {
+ foo(2, {x: 1}, {x: 1});
+}
+
+// Bail out and create a folded stub in `foo`.
+// The child matches, so we use `bar` as the owning script.
+for (var i = 0; i < 6; i++) {
+ var obj = {x: 1};
+ obj["y" + i] = 2;
+ foo(2, {y: 1, x: 2}, obj);
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1851911.js b/js/src/jit-test/tests/cacheir/bug1851911.js
new file mode 100644
index 0000000000..c066ed9c3e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1851911.js
@@ -0,0 +1,35 @@
+// |jit-test| --fast-warmup; --no-threads; --blinterp-eager
+
+function foo(o) {
+ return o.x;
+}
+
+function bar(n) {
+ with ({}) {}
+ class C {}
+ for (var i = 0; i < n; i++) {
+ var c = new C();
+ c.x = 0;
+ foo(c);
+ }
+}
+
+with ({}) {}
+
+function triggerIonCompile() {
+ for (var i = 0; i < 10; i++) {
+ bar(3);
+ }
+}
+
+triggerIonCompile();
+
+// Fill up shape list
+for (var i = 0; i < 6; i++) {
+ bar(10);
+}
+
+bar(10);
+gc();
+
+triggerIonCompile();
diff --git a/js/src/jit-test/tests/cacheir/bug1852893-1.js b/js/src/jit-test/tests/cacheir/bug1852893-1.js
new file mode 100644
index 0000000000..150c6252ae
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1852893-1.js
@@ -0,0 +1,27 @@
+// |jit-test| --fast-warmup; --no-threads; --blinterp-eager
+
+function foo(o) {
+ return foo_inner(o);
+}
+
+function foo_inner(o) {
+ return o.x;
+}
+with ({}) {}
+
+for (var i = 0; i < 13; i++) {
+ foo({x:1, ["y" + (i % 5)]: 2});
+}
+
+for (var i = 0; i < 17; i++) {
+ foo({x: 1, ["y" + (i % 8)]: 2});
+}
+
+eval("for (var i = 0; i < 10; i++) foo({y: 1, x:2})")
+
+// Purge a stub inside a trial-inlined script.
+gc();
+
+for (var i = 0; i < 50; i++) {
+ foo({x: 1, ["y" + (i % 3)]: 2});
+}
diff --git a/js/src/jit-test/tests/cacheir/bug1852893-2.js b/js/src/jit-test/tests/cacheir/bug1852893-2.js
new file mode 100644
index 0000000000..daf1d2d8b9
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1852893-2.js
@@ -0,0 +1,42 @@
+// |jit-test| --fast-warmup; --no-threads; --blinterp-eager
+
+function foo(o) {
+ return foo_inner(o);
+}
+
+function foo_inner(o) {
+ return o.x;
+}
+
+function bar(n) {
+ with ({}) {}
+ class C {}
+ for (var i = 0; i < n; i++) {
+ var c = new C();
+ c.x = 0;
+ foo(c);
+ }
+}
+
+with ({}) {}
+
+function triggerIonCompile() {
+ for (var i = 0; i < 10; i++) {
+ bar(3);
+ }
+}
+
+triggerIonCompile();
+
+// Fill up shape list
+for (var i = 0; i < 6; i++) {
+ bar(10);
+}
+
+// Overflow shape list, adding a new baseline IC.
+bar(10);
+
+// Purge a stub inside a monomorphically-inlined script.
+gc();
+
+triggerIonCompile();
diff --git a/js/src/jit-test/tests/cacheir/call-any-native.js b/js/src/jit-test/tests/cacheir/call-any-native.js
new file mode 100644
index 0000000000..7af6d2102c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/call-any-native.js
@@ -0,0 +1,59 @@
+// Test non-constructor calls
+
+var funcs = [Math.max, Math.min, Math.floor, Math.ceil, Math.sin,
+ Math.cos, Math.tan, Math.log, Math.acos, Math.asin];
+
+// Calculate expected values
+var expected = [Math.max(0.5, 2), Math.min(0.5, 2),
+ Math.floor(0.5, 2), Math.ceil(0.5, 2),
+ Math.sin(0.5, 2), Math.cos(0.5, 2),
+ Math.tan(0.5, 2), Math.log(0.5, 2),
+ Math.acos(0.5, 2), Math.asin(0.5, 2)];
+
+// Test a polymorphic call site
+for (var n = 0; n < 50; n++) {
+ for (var i = 0; i < funcs.length; i++) {
+ assertEq(funcs[i](0.5, 2), expected[i]);
+ }
+}
+
+// Test a polymorphic spread call site
+var spreadinput = [0.5, 2];
+for (var n = 0; n < 50; n++) {
+ for (var i = 0; i < funcs.length; i++) {
+ assertEq(funcs[i](...spreadinput), expected[i]);
+ }
+}
+
+// Test constructors
+
+function f1(x) {this[0] = x; this.length = 3;}
+function f2(x) {this[0] = x; this.length = 3;}
+function f3(x) {this[0] = x; this.length = 3;}
+function f4(x) {this[0] = x; this.length = 3;}
+function f5(x) {this[0] = x; this.length = 3;}
+function f6(x) {this[0] = x; this.length = 3;}
+function f7(x) {this[0] = x; this.length = 3;}
+function f8(x) {this[0] = x; this.length = 3;}
+function f9(x) {this[0] = x; this.length = 3;}
+
+var constructors = [f1,f2,f3,f4,f5,f6,f7,f8,f9,Array];
+
+// Test a polymorphic constructor site
+for (var n = 0; n < 50; n++) {
+ for (var i = 0; i < constructors.length; i++) {
+ let x = new constructors[i](1,2,3);
+ assertEq(x.length, 3);
+ assertEq(x[0], 1);
+ }
+}
+
+var constructorinput = [1,2,3];
+// Test a polymorphic spread constructor site
+for (var n = 0; n < 50; n++) {
+ for (var i = 0; i < constructors.length; i++) {
+ let x = new constructors[i](...constructorinput);
+ assertEq(x.length, 3);
+ assertEq(x[0], 1);
+ }
+}
diff --git a/js/src/jit-test/tests/cacheir/call-bound-function-many-args.js b/js/src/jit-test/tests/cacheir/call-bound-function-many-args.js
new file mode 100644
index 0000000000..1e17aa70e1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/call-bound-function-many-args.js
@@ -0,0 +1,63 @@
+function sum(...args) {
+ var res = 0;
+ for (var i = 0; i < args.length; i++) {
+ res += args[i];
+ }
+ return {v: res};
+}
+
+function testManyArgs1() {
+ var bound = sum.bind(null);
+ for (var i = 0; i < 20; i++) {
+ assertEq(bound(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
+ 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255).v,
+ 32640);
+ assertEq(new bound(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
+ 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255).v,
+ 32640);
+ }
+}
+testManyArgs1();
+
+function testManyArgs2() {
+ var bound = sum.bind(null, 1, 2, 3, 4, 5, 6);
+ for (var i = 0; i < 20; i++) {
+ assertEq(bound(7, 8, 9, 10, 11, 12).v, 78);
+ assertEq(bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20).v, 210);
+ assertEq(bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29).v, 435);
+ assertEq(new bound(7, 8, 9, 10, 11, 12).v, 78);
+ assertEq(new bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20).v, 210);
+ assertEq(new bound(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29).v, 435);
+ }
+}
+testManyArgs2();
diff --git a/js/src/jit-test/tests/cacheir/call-bound-scripted.js b/js/src/jit-test/tests/cacheir/call-bound-scripted.js
new file mode 100644
index 0000000000..960ca1f8ac
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/call-bound-scripted.js
@@ -0,0 +1,84 @@
+function testNumBoundArgsGuard() {
+ function f(a, b, c, d, e, f, g, h) {
+ return [a, b, c, d, e, f, g, h].join(",");
+ }
+ var arr = [
+ f.bind(null, 1, 2, 3, 4, 5, 6),
+ f.bind(null, 4, 5, 6, 7, 8, 9),
+ f.bind(null, 1, 2),
+ f.bind(null, 1),
+ ];
+ var expected = [
+ "1,2,3,4,5,6,10,11",
+ "4,5,6,7,8,9,10,11",
+ "1,2,10,11,12,13,14,15",
+ "1,10,11,12,13,14,15,",
+ ];
+ for (var i = 0; i < 100; i++) {
+ assertEq(arr[0](10, 11, 12, 13, 14, 15), expected[0]);
+ assertEq(arr[1](10, 11, 12, 13, 14, 15), expected[1]);
+ assertEq(arr[2](10, 11, 12, 13, 14, 15), expected[2]);
+ assertEq(arr[3](10, 11, 12, 13, 14, 15), expected[3]);
+ assertEq(arr[i % arr.length](10, 11, 12, 13, 14, 15), expected[i % arr.length]);
+ }
+}
+testNumBoundArgsGuard();
+
+function testJSFunctionGuard() {
+ var arr = [
+ (x => x * 0).bind(null, 1),
+ (x => x * 1).bind(null, 2),
+ (x => x * 2).bind(null, 3),
+ (x => x * 3).bind(null, 4),
+ (x => x * 4).bind(null, 5),
+ (x => x * 5).bind(null, 6),
+ (x => x * 6).bind(null, 7),
+ (x => x * 7).bind(null, 8),
+ (x => x * 8).bind(null, 9),
+ ];
+ var boundNative = Math.abs.bind(null, -123);
+ var boundProxy = (new Proxy(x => x * 42, {})).bind(null, 7);
+
+ for (var i = 0; i < 100; i++) {
+ var idx = i % arr.length;
+ var fun1 = arr[idx];
+ var expected1 = idx * (idx + 1);
+ assertEq(fun1(), expected1);
+ var fun2 = i > 70 ? boundNative : fun1;
+ assertEq(fun2(), i > 70 ? 123 : expected1);
+ var fun3 = i > 70 ? boundProxy : fun1;
+ assertEq(fun3(), i > 70 ? 294 : expected1);
+ }
+}
+testJSFunctionGuard();
+
+function testCrossRealmTarget() {
+ globalThis.num = "FAIL";
+
+ var globals = [];
+ for (var i = 0; i < 4; i++) {
+ var g = newGlobal({sameCompartmentAs: this});
+ g.num = i;
+ globals.push(g);
+ }
+ var arr = globals.map(g => g.evaluate("(x => this.num)"));
+ arr = arr.map(f => Function.prototype.bind.call(f, null));
+
+ for (var i = 0; i < 200; i++) {
+ assertEq(arr[0](), 0);
+ var idx = i % arr.length;
+ assertEq(arr[idx](), idx);
+ }
+}
+testCrossRealmTarget();
+
+// Bug 1819651.
+function testReturnsItself() {
+ var fun = function() { return boundFun; };
+ var boundFun = fun.bind(null);
+ for (var i = 0; i < 20; i++) {
+ assertEq(boundFun(), boundFun);
+ assertEq(new boundFun(), boundFun);
+ }
+}
+testReturnsItself();
diff --git a/js/src/jit-test/tests/cacheir/call-native-get-element-super.js b/js/src/jit-test/tests/cacheir/call-native-get-element-super.js
new file mode 100644
index 0000000000..c19baab62c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/call-native-get-element-super.js
@@ -0,0 +1,44 @@
+// |jit-test| --fast-warmup; --no-threads
+
+const target = {};
+const receiver = {};
+
+let count = 0;
+function getter() {
+ count++;
+ assertEq(this, receiver);
+}
+
+var x = Math.hypot(1,0);
+var y = 2**31 - 1;
+var z = -1;
+
+Object.defineProperty(target, x, { get: getter });
+Object.defineProperty(target, y, { get: getter });
+Object.defineProperty(target, z, { get: getter });
+
+function get(id) {
+ return Reflect.get(target, id, receiver);
+}
+
+function test() {
+ with ({}) {}
+ count = 0;
+ for (var i = 0; i < 100; i++) {
+ get(x);
+ get(y);
+ get(z);
+ }
+ assertEq(count, 300);
+}
+
+// Test baseline IC / transpiled to MIR
+test();
+
+// Force an IC in Ion.
+for (var i = 0; i < 10; i++) {
+ get("a");
+}
+
+// Test ion IC
+test();
diff --git a/js/src/jit-test/tests/cacheir/ccw-missing.js b/js/src/jit-test/tests/cacheir/ccw-missing.js
new file mode 100644
index 0000000000..275b998558
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/ccw-missing.js
@@ -0,0 +1,4 @@
+var wrapper = evaluate("var o = {}; o", {global: newGlobal({sameZoneAs: this})});
+for (var i = 0; i < 50; i++) {
+ assertEq(wrapper.x, undefined);
+}
diff --git a/js/src/jit-test/tests/cacheir/compare-null-or-undef.js b/js/src/jit-test/tests/cacheir/compare-null-or-undef.js
new file mode 100644
index 0000000000..b39eefd957
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/compare-null-or-undef.js
@@ -0,0 +1,93 @@
+// Test relational comparison when one operand is null or undefined.
+
+function test(xs) {
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i % xs.length];
+
+ // The result is equal when compared to the result with explicit ToNumber conversions.
+
+ // Test when null-or-undefined is on the right-hand side.
+ assertEq(x < nullOrUndef, (+x) < (+nullOrUndef));
+ assertEq(x <= nullOrUndef, (+x) <= (+nullOrUndef));
+ assertEq(x >= nullOrUndef, (+x) >= (+nullOrUndef));
+ assertEq(x > nullOrUndef, (+x) > (+nullOrUndef));
+
+ // Test when null-or-undefined is on the left-hand side.
+ assertEq(nullOrUndef < x, (+nullOrUndef) < (+x));
+ assertEq(nullOrUndef <= x, (+nullOrUndef) <= (+x));
+ assertEq(nullOrUndef >= x, (+nullOrUndef) >= (+x));
+ assertEq(nullOrUndef > x, (+nullOrUndef) > (+x));
+ }
+}
+
+function runTest(inputs) {
+ let fNull = Function(`return ${test}`.replaceAll("nullOrUndef", "null"))();
+ fNull(inputs);
+
+ let fUndefined = Function(`return ${test}`.replaceAll("nullOrUndef", "undefined"))();
+ fUndefined(inputs);
+}
+
+// Null inputs.
+runTest([
+ null,
+]);
+
+// Undefined inputs.
+runTest([
+ undefined,
+]);
+
+// Null or undefined inputs.
+runTest([
+ null,
+ undefined,
+]);
+
+// Int32 inputs
+runTest([
+ -0x8000_0000,
+ -0x7fff_ffff,
+ -0x7fff_fffe,
+ -2,
+ -1,
+ 0,
+ 1,
+ 2,
+ 0x7fff_fffe,
+ 0x7fff_ffff,
+]);
+
+// Number inputs
+runTest([
+ Number.NEGATIVE_INFINITY,
+ -Number.MAX_VALUE,
+ Number.MIN_SAFE_INTEGER,
+ -1.5,
+ -0.5,
+ -Number.EPSILON,
+ -Number.MIN_VALUE,
+ -0,
+ 0,
+ Number.MIN_VALUE,
+ Number.EPSILON,
+ 0.5,
+ 1.5,
+ Number.MAX_SAFE_INTEGER,
+ Number.MAX_VALUE,
+ Number.POSITIVE_INFINITY,
+ NaN,
+]);
+
+// Boolean inputs.
+runTest([
+ true, false,
+]);
+
+// String inputs
+runTest([
+ "",
+ "0", "-0", "0.0", "0e100",
+ "1", "1.5", "-1234", "-1e0",
+ "Infinity", "NaN", "not-a-number",
+]);
diff --git a/js/src/jit-test/tests/cacheir/compare.js b/js/src/jit-test/tests/cacheir/compare.js
new file mode 100644
index 0000000000..3656a7d0ee
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/compare.js
@@ -0,0 +1,292 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function warmup(fun, input_array) {
+ for (var index = 0; index < input_array.length; index++) {
+ input = input_array[index];
+ input_lhs = input[0];
+ input_rhs = input[1];
+ output = input[2];
+ for (var i = 0; i < 30; i++) {
+ var str = "";
+ var y = fun(input_lhs, input_rhs);
+ if (y != output) {
+ str = "Computed: "+y+ ", expected: "+ output + " ( " + fun + " lhs: "+ input_lhs + " rhs: " + input_rhs +")";
+ }
+ assertEq(str, "");
+ }
+ }
+}
+
+
+// Int32 + Int32
+var Int32Int32Fun_GT1 = (a, b) => { return a > b; }
+warmup(Int32Int32Fun_GT1, [[1,2, false], [1,1, false], [3,4, false], [4294967295, 2, true],
+ [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_GTE1 = (a, b) => { return a >= b; }
+warmup(Int32Int32Fun_GTE1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, true],
+ [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_LT1 = (a, b) => { return a < b; }
+warmup(Int32Int32Fun_LT1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, false],
+ [NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_LTE1 = (a, b) => { return a <= b; }
+warmup(Int32Int32Fun_LTE1, [[1,2, true], [1,1, true], [3,4, true], [4294967295, 2, false],
+ [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10, undefined, false]]);
+
+var Int32Int32Fun_EQ1 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false],
+ [NaN, 2, false], [-1000, NaN, false], [undefined, null, true],
+ ['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]);
+
+var Int32Int32Fun_EQ2 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]);
+
+var Int32Int32Fun_EQ3 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]);
+
+var Int32Int32Fun_EQ4 = (a, b) => { return a == b; }
+warmup(Int32Int32Fun_EQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, true], [undefined, null, true]]);
+
+
+var Int32Int32Fun_NEQ1 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true],
+ [NaN, 2, true], [-1000, NaN, true], [undefined, null, false],
+ ['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]);
+
+var Int32Int32Fun_NEQ2 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]);
+
+var Int32Int32Fun_NEQ3 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]);
+
+var Int32Int32Fun_NEQ4 = (a, b) => { return a != b; }
+warmup(Int32Int32Fun_NEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, false], [undefined, null, false]]);
+
+var Int32Int32Fun_SEQ1 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ1, [[1,2, false], [1,1, true], [3,4, false], [4294967295, 2, false],
+ [NaN, 2, false], [-1000, NaN, false], [undefined, null, false],
+ ['0', 0, false], [new String('0'), 0, false], [10, undefined, false]]);
+
+var Int32Int32Fun_SEQ2 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ2, [[1, NaN, false], [1, undefined, false], [1, null, false]]);
+
+var Int32Int32Fun_SEQ3 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ3, [[{a: 1}, NaN, false], [{a: 1}, undefined, false], [{a: 1}, null, false]]);
+
+var Int32Int32Fun_SEQ4 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ4, [[undefined, undefined, true], [null, null, true], [null, undefined, false], [undefined, null, false] ]);
+
+var Int32Int32Fun_SEQ5 = (a, b) => { return a === b; }
+warmup(Int32Int32Fun_SEQ5, [[1, true, false], [1, false, false], [false, 1, false], [true, 0, false], [true, true, true]]);
+
+var Int32Int32Fun_SNEQ1 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ1, [[1,2, true], [1,1, false], [3,4, true], [4294967295, 2, true],
+ [NaN, 2, true], [-1000, NaN, true], [undefined, null, true],
+ ['0', 0, true], [new String('0'), 0, true], [10, undefined, true]]);
+
+var Int32Int32Fun_SNEQ2 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ2, [[1, NaN, true], [1, undefined, true], [1, null, true]]);
+
+var Int32Int32Fun_SNEQ3 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ3, [[{a: 1}, NaN, true], [{a: 1}, undefined, true], [{a: 1}, null, true]]);
+
+var Int32Int32Fun_SNEQ4 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ4, [[undefined, undefined, false], [null, null, false], [null, undefined, true], [undefined, null, true] ]);
+
+var Int32Int32Fun_SNEQ5 = (a, b) => { return a !== b; }
+warmup(Int32Int32Fun_SNEQ5, [[1, true, true], [1, false, true], [false, 1, true], [true, 0, true]]);
+
+
+// Number Number
+var NumberNumberFun_GT1 = (a, b) => { return a > b; }
+warmup(NumberNumberFun_GT1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+ [1,1, false], [3,4, false], [4294967295, 2, true],
+ [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_GTE1 = (a, b) => { return a >= b; }
+warmup(NumberNumberFun_GTE1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+ [1,1, true], [3,4, false], [4294967295, 2, true],
+ [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_LT1 = (a, b) => { return a < b; }
+warmup(NumberNumberFun_LT1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+ [1,1, false], [3,4, true], [4294967295, 2, false],
+ [NaN, 2, false],[-1000, NaN, false], [NaN, NaN, false, [10.2, undefined, false]]]);
+
+var NumberNumberFun_LTE1 = (a, b) => { return a <= b; }
+warmup(NumberNumberFun_LTE1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+ [1,1, true], [3,4, true], [4294967295, 2, false],
+ [NaN, 2, false], [-1000, NaN, false], [NaN, NaN, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_EQ1 = (a, b) => { return a == b; }
+warmup(NumberNumberFun_EQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+ [1,1, true], [3,4, false], [4294967295, 2, false],
+ [NaN, 2, false], [-1000, NaN, false], [undefined, null, true],
+ ['0', 0, true], [new String('0'), 0, true], [10.2, undefined, false]]);
+
+var NumberNumberFun_NEQ1 = (a, b) => { return a != b; }
+warmup(NumberNumberFun_NEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+ [1,1, false], [3,4, true], [4294967295, 2, true],
+ [NaN, 2, true], [-1000, NaN, true], [undefined, null, false],
+ ['0', 0, false], [new String('0'), 0, false], [10.2, undefined, true]]);
+
+var NumberNumberFun_SEQ1 = (a, b) => { return a === b; }
+warmup(NumberNumberFun_SEQ1, [[1,2, false], [1.3, 2, false], [1, 2.6, false], [1.2,2.2, false],
+ [1,1, true], [3,4, false], [4294967295, 2, false],
+ [NaN, 2, false], [-1000, NaN, false], [undefined, null, false],
+ ['0', 0, false], [new String('0'), 0, false], [10.2, undefined, false]]);
+
+var NumberNumberFun_SNEQ1 = (a, b) => { return a !== b; }
+warmup(NumberNumberFun_SNEQ1, [[1,2, true], [1.3, 2, true], [1, 2.6, true], [1.2,2.2, true],
+ [1,1, false], [3,4, true], [4294967295, 2, true],
+ [NaN, 2, true], [-1000, NaN, true], [undefined, null, true],
+ ['0', 0, true], [new String('0'), 0, true], [10.2, undefined, true]]);
+
+// Boolean + Int32
+var BooleanFun_GT1 = (a, b) => { return a > b; }
+warmup(BooleanFun_GT1, [[1,2, false], [true, 2, false], [1,1, false], [true,true, false], [3,4, false], ]);
+
+var BooleanFun_GTE1 = (a, b) => { return a >= b; }
+warmup(BooleanFun_GTE1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]);
+
+var BooleanFun_LT1 = (a, b) => { return a < b; }
+warmup(BooleanFun_LT1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]);
+
+var BooleanFun_LTE1 = (a, b) => { return a <= b; }
+warmup(BooleanFun_LTE1, [[1,2, true], [true, 2, true], [1,1, true], [true,true, true], [3,4, true]]);
+
+var BooleanFun_EQ1 = (a, b) => { return a == b; }
+warmup(BooleanFun_EQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false],
+ ['0', 0, true], [new String('0'), 0, true], [10, undefined, false]]);
+
+var BooleanFun_NEQ1 = (a, b) => { return a != b; }
+warmup(BooleanFun_NEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true],
+ ['0', 0, false], [new String('0'), 0, false], [10, undefined, true]]);
+
+var BooleanFun_SEQ1 = (a, b) => { return a === b; }
+warmup(BooleanFun_SEQ1, [[1,2, false], [true, 2, false], [1,1, true], [true,true, true], [3,4, false]]);
+
+var BooleanFun_SNEQ1 = (a, b) => { return a !== b; }
+warmup(BooleanFun_SNEQ1, [[1,2, true], [true, 2, true], [1,1, false], [true,true, false], [3,4, true]]);
+
+// Undefined / Null equality.
+var UndefNull_EQ1 = (a, b) => { return a == b; }
+warmup(UndefNull_EQ1, [[undefined, null, true], [undefined, undefined, true], [null, undefined, true],
+ [null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]);
+
+var UndefNull_NEQ1 = (a, b) => { return a != b; }
+warmup(UndefNull_NEQ1, [[undefined, null, false], [undefined, undefined, false], [null, undefined, false],
+ [null, null, false], [{a: 10}, undefined, true], [{a: 10}, null, true]]);
+
+var UndefNull_SEQ1 = (a, b) => { return a === b; }
+warmup(UndefNull_SEQ1, [[undefined, null, false], [undefined, undefined, true], [null, undefined, false],
+ [null, null, true], [{a: 10}, undefined, false], [{a: 10}, null, false]]);
+
+var UndefNull_SNEQ1 = (a, b) => { return a !== b; }
+warmup(UndefNull_SNEQ1, [[undefined, null, true], [undefined, undefined, false], [null, undefined, true],
+ [null, null, false], [{a: 10}, undefined, true], [{a: 10}, null, true]]);
+
+var typeDifference = function(a,b) { return a === b; }
+warmup(typeDifference, [[1,1, true], [3,3, true], [3, typeDifference, false],[typeDifference, {}, false],
+ [3.2, 1, false], [0, -0, true]]);
+
+// String + Number
+var String_Number_GT1 = (a, b) => { return a > b; }
+warmup(String_Number_GT1, [["1.4",2, false], [1,"1", false], ["3",4, false],
+ [-1024, "-1023", false], [1.3, "1.2", true]]);
+
+var String_Number_GTE1 = (a, b) => { return a >= b; }
+warmup(String_Number_GTE1, [["1.4",2, false], [1,"1", true], [3,"4", false],
+ [-1024, "-1023", false], [1.2, "1.2", true]]);
+
+var String_Number_LT1 = (a, b) => { return a < b; }
+warmup(String_Number_LT1, [["1.4",2, true], ["1",1, false], [3,"4", true],
+ [-1024, "-1023", true], [1.2, "1.2", false]]);
+
+var String_Number_LTE1 = (a, b) => { return a <= b; }
+warmup(String_Number_LTE1, [["1.4",2, true], ["1",1, true], [3,"4", true],
+ [-1024, "-1023", true], [1.2, "1.2", true]]);
+
+var String_Number_EQ1 = (a, b) => { return a == b; }
+warmup(String_Number_EQ1, [["1.4",2, false], ["1",1, true], [3,"4", false],
+ [-1024, "-1023", false], [1.2, "1.2", true]]);
+
+var String_Number_NEQ1 = (a, b) => { return a != b; }
+warmup(String_Number_NEQ1, [["1.4",2, true], ["1",1, false], [3,"4", true],
+ [-1024, "-1023", true], [1.2, "1.2", false]]);
+
+var String_Number_SEQ1 = (a, b) => { return a === b; }
+warmup(String_Number_SEQ1, [["1.4",2, false], ["1",1, false], [3,"4", false],
+ [-1024, "-1023", false], [1.2, "1.2", false]]);
+
+var String_Number_SNEQ1 = (a, b) => { return a !== b; }
+warmup(String_Number_SNEQ1, [["1.4",2, true], ["1",1, true], [3,"4", true],
+ [-1024, "-1023", true], [1.2, "1.2", true]]);
+
+// String + String
+var String_String_GT1 = (a, b) => a > b;
+warmup(String_String_GT1, [["", "", false], ["a", "a", false], ["aa", "aa", false],
+ ["a", "", true], ["", "a", false],
+ ["a", "b", false], ["b", "a", true],
+ ["a", "ab", false], ["ab", "a", true],
+ ]);
+
+var String_String_GE1 = (a, b) => a >= b;
+warmup(String_String_GE1, [["", "", true], ["a", "a", true], ["aa", "aa", true],
+ ["a", "", true], ["", "a", false],
+ ["a", "b", false], ["b", "a", true],
+ ["a", "ab", false], ["ab", "a", true],
+ ]);
+
+var String_String_LT1 = (a, b) => a < b;
+warmup(String_String_LT1, [["", "", false], ["a", "a", false], ["aa", "aa", false],
+ ["a", "", false], ["", "a", true],
+ ["a", "b", true], ["b", "a", false],
+ ["a", "ab", true], ["ab", "a", false],
+ ]);
+
+var String_String_LE1 = (a, b) => a <= b;
+warmup(String_String_LE1, [["", "", true], ["a", "a", true], ["aa", "aa", true],
+ ["a", "", false], ["", "a", true],
+ ["a", "b", true], ["b", "a", false],
+ ["a", "ab", true], ["ab", "a", false],
+ ]);
+
+var String_String_EQ1 = (a, b) => a == b;
+warmup(String_String_EQ1, [["", "", true], ["a", "a", true], ["aa", "aa", true],
+ ["a", "", false], ["", "a", false],
+ ["a", "b", false], ["b", "a", false],
+ ["a", "ab", false], ["ab", "a", false],
+ ]);
+
+var String_String_SEQ1 = (a, b) => a === b;
+warmup(String_String_SEQ1, [["", "", true], ["a", "a", true], ["aa", "aa", true],
+ ["a", "", false], ["", "a", false],
+ ["a", "b", false], ["b", "a", false],
+ ["a", "ab", false], ["ab", "a", false],
+ ]);
+
+var String_String_NE1 = (a, b) => a != b;
+warmup(String_String_NE1, [["", "", false], ["a", "a", false], ["aa", "aa", false],
+ ["a", "", true], ["", "a", true],
+ ["a", "b", true], ["b", "a", true],
+ ["a", "ab", true], ["ab", "a", true],
+ ]);
+
+var String_String_SNE1 = (a, b) => a !== b;
+warmup(String_String_SNE1, [["", "", false], ["a", "a", false], ["aa", "aa", false],
+ ["a", "", true], ["", "a", true],
+ ["a", "b", true], ["b", "a", true],
+ ["a", "ab", true], ["ab", "a", true],
+ ]);
+
+// IsHTMLDDA internal slot
+// https://tc39.github.io/ecma262/#sec-IsHTMLDDA-internal-slot
+var IsHTMLDDA_EQ1 = (a, b) => a == b;
+warmup(IsHTMLDDA_EQ1, [[createIsHTMLDDA(), null, true],
+ [createIsHTMLDDA(), undefined, true],
+ [null, createIsHTMLDDA(), true],
+ [undefined, createIsHTMLDDA(), true],
+ ]);
diff --git a/js/src/jit-test/tests/cacheir/construct-bound-realm.js b/js/src/jit-test/tests/cacheir/construct-bound-realm.js
new file mode 100644
index 0000000000..686fdc6c11
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/construct-bound-realm.js
@@ -0,0 +1,10 @@
+function f() {
+ var g = newGlobal({sameCompartmentAs: this});
+ g.evaluate(`function f() {}`);
+ var boundF = g.f.bind(null);
+ for (var i = 0; i < 50; i++) {
+ var obj = new boundF();
+ assertEq(objectGlobal(obj), g);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js b/js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js
new file mode 100644
index 0000000000..38c7a998b5
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/dataview-non-number-value-set.js
@@ -0,0 +1,126 @@
+const types = [
+ "Int8",
+ "Int16",
+ "Int32",
+ "Uint8",
+ "Uint16",
+ "Uint32",
+ "Float32",
+ "Float64",
+];
+
+function convert(type, value) {
+ let num = Number(value);
+ switch (type) {
+ case "Int8":
+ return ((num | 0) << 24) >> 24;
+ case "Int16":
+ return ((num | 0) << 16) >> 16;
+ case "Int32":
+ return (num | 0);
+ case "Uint8":
+ return (num >>> 0) & 0xff;
+ case "Uint16":
+ return (num >>> 0) & 0xffff;
+ case "Uint32":
+ return (num >>> 0);
+ case "Uint8Clamped": {
+ if (Number.isNaN(num)) {
+ return 0;
+ }
+ let clamped = Math.max(0, Math.min(num, 255));
+ let f = Math.floor(clamped);
+ if (clamped < f + 0.5) {
+ return f;
+ }
+ if (clamped > f + 0.5) {
+ return f + 1;
+ }
+ return f + (f & 1);
+ }
+ case "Float32":
+ return Math.fround(num);
+ case "Float64":
+ return num;
+ }
+ throw new Error();
+}
+
+
+function runTest(type, initial, values) {
+ let expected = values.map(v => convert(type, v));
+ assertEq(
+ expected.some(e => Object.is(e, initial)),
+ false,
+ "initial must be different from the expected values"
+ );
+
+ // Create a fresh function to ensure ICs are specific to a single DataView function.
+ let test = Function("initial, values, expected", `
+ let ab = new ArrayBuffer(16);
+ let dv = new DataView(ab);
+ for (let i = 0; i < 200; ++i) {
+ dv.set${type}(0, initial);
+ dv.set${type}(0, values[i % values.length]);
+ assertEq(dv.get${type}(0), expected[i % expected.length]);
+ }
+ `);
+ test(initial, values, expected);
+}
+
+const tests = [
+ // |null| is coerced to zero.
+ {
+ initial: 1,
+ values: [null],
+ },
+
+ // |undefined| is coerced to zero or NaN.
+ {
+ initial: 1,
+ values: [undefined],
+ },
+
+ // |false| is coerced to zero and |true| is coerced to one.
+ {
+ initial: 2,
+ values: [false, true],
+ },
+
+ // Strings without a fractional part.
+ {
+ initial: 42,
+ values: [
+ "0", "1", "10", "111", "128", "256", "0x7fffffff", "0xffffffff",
+ ],
+ },
+
+ // Strings without a fractional part, but a leading minus sign.
+ {
+ initial: 42,
+ values: [
+ "-0", "-1", "-10", "-111", "-128", "-256", "-2147483647", "-4294967295",
+ ],
+ },
+
+ // Strings with a fractional number part.
+ {
+ initial: 42,
+ values: [
+ "0.1", "1.2", "10.8", "111.9",
+ "-0.1", "-1.2", "-10.8", "-111.9",
+ ],
+ },
+
+ // Special values and strings not parseable as a number.
+ {
+ initial: 42,
+ values: ["Infinity", "-Infinity", "NaN", "foobar"],
+ },
+];
+
+for (let type of types) {
+ for (let {initial, values} of tests) {
+ runTest(type, initial, values);
+ }
+}
diff --git a/js/src/jit-test/tests/cacheir/dom-call.js b/js/src/jit-test/tests/cacheir/dom-call.js
new file mode 100644
index 0000000000..e796bbbadd
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/dom-call.js
@@ -0,0 +1,29 @@
+function simple() {
+ var obj = new FakeDOMObject();
+ for (var i = 0; i < 10; i++) {
+ assertEq(obj.doFoo(0, 0, 0), 3);
+ }
+}
+
+function wrongThis() {
+ var obj = new FakeDOMObject();
+ var wrong = {doFoo: obj.doFoo};
+
+ for (var i = 0; i < 100; i++) {
+ assertEq(obj.doFoo(0, 0), i <= 50 ? 2 : undefined);
+ if (i == 50) {
+ obj = wrong;
+ }
+ }
+}
+
+function spread() {
+ var obj = new FakeDOMObject();
+ for (var i = 0; i < 10; i++) {
+ assertEq(obj.doFoo(...[1, 2, 3, 4]), 4);
+ }
+}
+
+simple();
+wrongThis();
+spread(); \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js
new file mode 100644
index 0000000000..6558176f54
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-1.js
@@ -0,0 +1,38 @@
+// Function.prototype.apply is inlined as Function.prototype.call when it's
+// called with less than two arguments.
+//
+// Test monomorphic calls.
+
+function testThisAbsent() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Math.min.apply();
+ assertEq(r, Infinity);
+ }
+}
+for (let i = 0; i < 2; ++i) testThisAbsent();
+
+function test0() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Math.min.apply(null);
+ assertEq(r, Infinity);
+ }
+}
+for (let i = 0; i < 2; ++i) test0();
+
+// NOTE: We don't yet inline the case when the second argument is |null|.
+function test1Null() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Math.min.apply(null, null);
+ assertEq(r, Infinity);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Null();
+
+// NOTE: We don't yet inline the case when the second argument is |undefined|.
+function test1Undefined() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Math.min.apply(null, undefined);
+ assertEq(r, Infinity);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Undefined();
diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js
new file mode 100644
index 0000000000..b1295c99bd
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-2.js
@@ -0,0 +1,46 @@
+// Function.prototype.apply is inlined as Function.prototype.call when it's
+// called with less than two arguments.
+//
+// Test polymorphic calls.
+
+function testThisAbsent() {
+ let xs = [Math.min, Math.max];
+ let ys = [Infinity, -Infinity];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply();
+ assertEq(r, ys[i & 1]);
+ }
+}
+for (let i = 0; i < 2; ++i) testThisAbsent();
+
+function test0() {
+ let xs = [Math.min, Math.max];
+ let ys = [Infinity, -Infinity];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply(null);
+ assertEq(r, ys[i & 1]);
+ }
+}
+for (let i = 0; i < 2; ++i) test0();
+
+// NOTE: We don't yet inline the case when the second argument is |null|.
+function test1Null() {
+ let xs = [Math.min, Math.max];
+ let ys = [Infinity, -Infinity];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply(null, null);
+ assertEq(r, ys[i & 1]);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Null();
+
+// NOTE: We don't yet inline the case when the second argument is |undefined|.
+function test1Undefined() {
+ let xs = [Math.min, Math.max];
+ let ys = [Infinity, -Infinity];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply(null, undefined);
+ assertEq(r, ys[i & 1]);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Undefined();
diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js
new file mode 100644
index 0000000000..5d3cee4afa
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-native-3.js
@@ -0,0 +1,39 @@
+// Function.prototype.apply is inlined as Function.prototype.call when it's
+// called with less than two arguments.
+//
+// Test inlining through specialised natives.
+
+// NOTE: We don't inline when |apply| is called with zero arguments.
+function testThisAbsent() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Array.apply();
+ assertEq(r.length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) testThisAbsent();
+
+function test0() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Array.apply(null);
+ assertEq(r.length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) test0();
+
+// NOTE: We don't yet inline the case when the second argument is |null|.
+function test1Null() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Array.apply(null, null);
+ assertEq(r.length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Null();
+
+// NOTE: We don't yet inline the case when the second argument is |undefined|.
+function test1Undefined() {
+ for (let i = 0; i < 200; ++i) {
+ let r = Array.apply(null, undefined);
+ assertEq(r.length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Undefined();
diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js
new file mode 100644
index 0000000000..a0227ba81a
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-1.js
@@ -0,0 +1,42 @@
+// Function.prototype.apply is inlined as Function.prototype.call when it's
+// called with less than two arguments.
+//
+// Test monomorphic calls.
+
+function one() {
+ return 1;
+}
+
+function testThisAbsent() {
+ for (let i = 0; i < 200; ++i) {
+ let r = one.apply();
+ assertEq(r, 1);
+ }
+}
+for (let i = 0; i < 2; ++i) testThisAbsent();
+
+function test0() {
+ for (let i = 0; i < 200; ++i) {
+ let r = one.apply(null);
+ assertEq(r, 1);
+ }
+}
+for (let i = 0; i < 2; ++i) test0();
+
+// NOTE: We don't yet inline the case when the second argument is |null|.
+function test1Null() {
+ for (let i = 0; i < 200; ++i) {
+ let r = one.apply(null, null);
+ assertEq(r, 1);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Null();
+
+// NOTE: We don't yet inline the case when the second argument is |undefined|.
+function test1Undefined() {
+ for (let i = 0; i < 200; ++i) {
+ let r = one.apply(null, undefined);
+ assertEq(r, 1);
+ }
+}
+for (let i = 0; i < 2; ++i) test1Undefined();
diff --git a/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js
new file mode 100644
index 0000000000..39ff35d96b
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-apply-as-call-scripted-2.js
@@ -0,0 +1,50 @@
+// Function.prototype.apply is inlined as Function.prototype.call when it's
+// called with less than two arguments.
+//
+// Test polymorphic calls.
+
+function one() {
+ return 1;
+}
+
+function two() {
+ return 2;
+}
+
+function testThisAbsent() {
+ let xs = [one, two];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply();
+ assertEq(r, 1 + (i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testThisAbsent();
+
+function test0() {
+ let xs = [one, two];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply(null);
+ assertEq(r, 1 + (i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) test0();
+
+// NOTE: We don't yet inline the case when the second argument is |null|.
+function test1Null() {
+ let xs = [one, two];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply(null, null);
+ assertEq(r, 1 + (i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) test1Null();
+
+// NOTE: We don't yet inline the case when the second argument is |undefined|.
+function test1Undefined() {
+ let xs = [one, two];
+ for (let i = 0; i < 200; ++i) {
+ let r = xs[i & 1].apply(null, undefined);
+ assertEq(r, 1 + (i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) test1Undefined();
diff --git a/js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js b/js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js
new file mode 100644
index 0000000000..b310df577b
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-apply-null-undefined.js
@@ -0,0 +1,36 @@
+function testBasic() {
+ var thisVal = {};
+ var arr = [1, 2, 3];
+ var f = function() {
+ assertEq(this, thisVal);
+ assertEq(arguments.length, 0);
+ return 456;
+ };
+ for (var i = 0; i < 100; i++) {
+ // Scripted callee.
+ assertEq(f.apply(thisVal), 456);
+ assertEq(f.apply(thisVal), 456);
+ assertEq(f.apply(thisVal, null), 456);
+ assertEq(f.apply(thisVal, undefined), 456);
+
+ // Native callee.
+ assertEq(Math.abs.apply(), NaN);
+ assertEq(Math.abs.apply(null), NaN);
+ assertEq(Math.abs.apply(null, null), NaN);
+ assertEq(Array.prototype.join.apply(arr), "1,2,3");
+ assertEq(Array.prototype.join.apply(arr, null), "1,2,3");
+ assertEq(Array.prototype.join.apply(arr, undefined), "1,2,3");
+ }
+}
+testBasic();
+
+function testUndefinedGuard() {
+ var f = function() { return arguments.length; }
+ var arr = [-5, 5];
+ for (var i = 0; i < 100; i++) {
+ var args = i < 50 ? undefined : arr;
+ assertEq(f.apply(null, args), i < 50 ? 0 : 2);
+ assertEq(Math.abs.apply(null, args), i < 50 ? NaN : 5);
+ }
+}
+testUndefinedGuard();
diff --git a/js/src/jit-test/tests/cacheir/fun-call-apply-weird.js b/js/src/jit-test/tests/cacheir/fun-call-apply-weird.js
new file mode 100644
index 0000000000..ee1fe0e531
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-call-apply-weird.js
@@ -0,0 +1,31 @@
+// |jit-test| --fast-warmup
+
+// Function with overridden call/apply (scripted).
+function funOverridden1(x, y) { return x + y; }
+funOverridden1.call = x => x + 1;
+funOverridden1.apply = x => x + 2;
+
+// Function with overridden call/apply (native).
+function funOverridden2(x, y) { return x + y; }
+funOverridden2.call = Math.abs;
+funOverridden2.apply = Math.abs;
+
+// Function with call/apply properties with other names.
+function funOverridden3(x, y) { return x + y; }
+funOverridden3.myCall = Function.prototype.call;
+funOverridden3.myApply = Function.prototype.apply;
+
+function f() {
+ var arr = [1, 2];
+ for (var i = 0; i < 100; i++) {
+ assertEq(funOverridden1.call(i, i), i + 1);
+ assertEq(funOverridden1.apply(i, i), i + 2);
+
+ assertEq(funOverridden2.call(i, i), i);
+ assertEq(funOverridden2.apply(i, i), i);
+
+ assertEq(funOverridden3.myCall(null, i, i), i + i);
+ assertEq(funOverridden3.myApply(null, arr), 3);
+ }
+}
+f();
diff --git a/js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js b/js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js
new file mode 100644
index 0000000000..c7e6fd3183
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-call-inline-native-1.js
@@ -0,0 +1,59 @@
+// Test inlining natives through Function.prototype.call
+//
+// Math.min() is inlined when there are 1-4 arguments.
+
+function mathMinThisAbsent() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call();
+ assertEq(r, Infinity);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMinThisAbsent();
+
+function mathMin0() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call(null);
+ assertEq(r, Infinity);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMin0();
+
+function mathMin1() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call(null, i);
+ assertEq(r, i);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMin1();
+
+function mathMin2() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call(null, i, i + 1);
+ assertEq(r, i);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMin2();
+
+function mathMin3() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call(null, i, i + 1, i + 2);
+ assertEq(r, i);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMin3();
+
+function mathMin4() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call(null, i, i + 1, i + 2, i + 3);
+ assertEq(r, i);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMin4();
+
+function mathMin5() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Math.min.call(null, i, i + 1, i + 2, i + 3, i + 4);
+ assertEq(r, i);
+ }
+}
+for (let i = 0; i < 2; ++i) mathMin5();
diff --git a/js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js b/js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js
new file mode 100644
index 0000000000..13686e1119
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-call-inline-native-2.js
@@ -0,0 +1,20 @@
+// Test inlining natives through Function.prototype.call
+
+function test(fn, expected) {
+ for (let i = 0; i < 400; ++i) {
+ let r = fn.call(null, 0, 1);
+ assertEq(r, expected);
+ }
+}
+
+for (let i = 0; i < 2; ++i) {
+ let fn, expected;
+ if (i === 0) {
+ fn = Math.min;
+ expected = 0;
+ } else {
+ fn = Math.max;
+ expected = 1;
+ }
+ test(fn, expected);
+}
diff --git a/js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js b/js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js
new file mode 100644
index 0000000000..5d396d426e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/fun-call-inline-native-3.js
@@ -0,0 +1,35 @@
+// Test inlining natives through Function.prototype.call
+//
+// Array() is inlined when there are 0-1 arguments.
+
+function arrayThisAbsent() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Array.call();
+ assertEq(r.length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) arrayThisAbsent();
+
+function array0() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Array.call(null);
+ assertEq(r.length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) array0();
+
+function array1() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Array.call(null, 5);
+ assertEq(r.length, 5);
+ }
+}
+for (let i = 0; i < 2; ++i) array1();
+
+function array2() {
+ for (let i = 0; i < 400; ++i) {
+ let r = Array.call(null, 5, 10);
+ assertEq(r.length, 2);
+ }
+}
+for (let i = 0; i < 2; ++i) array2();
diff --git a/js/src/jit-test/tests/cacheir/function-length.js b/js/src/jit-test/tests/cacheir/function-length.js
new file mode 100644
index 0000000000..14e852683f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/function-length.js
@@ -0,0 +1,48 @@
+function interpreted() {
+ for (var i = 0; i < 50; i++) {
+ var f = function () {};
+ assertEq(f.length, 0);
+ }
+
+ for (var i = 0; i < 50; i++) {
+ var f = function (a, b) {};
+ assertEq(f.length, 2);
+ }
+}
+
+function bound() {
+ for (var i = 0; i < 50; i++) {
+ var f = (function () {}).bind({});
+ assertEq(f.length, 0);
+ }
+
+ for (var i = 0; i < 50; i++) {
+ var f = (function (a, b) {}).bind({});
+ assertEq(f.length, 2);
+ }
+}
+
+function native() {
+ for (var i = 0; i < 50; i++) {
+ // Use the interpreted function for getting the IC generated in the first place.
+ var f = function (a) {};
+
+ if (i == 15) {
+ f = Math.sin;
+ } else if (i == 20) {
+ f = Math.cos;
+ } else if (i == 25) {
+ f = Math.ceil;
+ } else if (i == 30) {
+ f = Math.tan;
+ } else if (i == 35) {
+ f = Math.tanh;
+ }
+
+ assertEq(f.length, 1);
+ }
+}
+
+interpreted();
+bound();
+native(); \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/function-name.js b/js/src/jit-test/tests/cacheir/function-name.js
new file mode 100644
index 0000000000..43b603c362
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/function-name.js
@@ -0,0 +1,59 @@
+function interpreted() {
+ for (var i = 0; i < 50; i++) {
+ var f = (0, function () {});
+ assertEq(f.name, "");
+ }
+
+ for (var i = 0; i < 50; i++) {
+ var f = function () {};
+ assertEq(f.name, "f");
+ }
+
+ for (var i = 0; i < 50; i++) {
+ var f = function a () {};
+ assertEq(f.name, "a");
+ }
+}
+
+function bound() {
+ for (var i = 0; i < 50; i++) {
+ var f = (function () {}).bind({});
+ assertEq(f.name, "bound ");
+ }
+
+ for (var i = 0; i < 50; i++) {
+ var f = (function a () {}).bind({});
+ assertEq(f.name, "bound a");
+ }
+}
+
+function native() {
+ for (var i = 0; i < 50; i++) {
+ // Use the interpreted function for getting the IC generated in the first place.
+ var f = function () {};
+
+ var name = "f";
+ if (i == 15) {
+ f = Math.sin;
+ name = "sin";
+ } else if (i == 20) {
+ f = Math.cos;
+ name = "cos";
+ } else if (i == 25) {
+ f = Math.ceil;
+ name = "ceil";
+ } else if (i == 30) {
+ f = Math.tan;
+ name = "tan";
+ } else if (i == 35) {
+ f = Math.tanh;
+ name = "tanh";
+ }
+
+ assertEq(f.name, name);
+ }
+}
+
+interpreted();
+bound();
+native();
diff --git a/js/src/jit-test/tests/cacheir/generic-spreadcall.js b/js/src/jit-test/tests/cacheir/generic-spreadcall.js
new file mode 100644
index 0000000000..bf1b053a53
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/generic-spreadcall.js
@@ -0,0 +1,14 @@
+var sum = 0;
+
+function foo() { sum++; }
+
+const MAX_JIT_ARGS = 4096 / 8;
+var arr = [];
+for (var i = 0; i < MAX_JIT_ARGS + 1; i++) {
+ arr.push(1);
+}
+
+for (var i = 0; i < 275; i++) {
+ foo(...arr);
+}
+assertEq(sum, 275);
diff --git a/js/src/jit-test/tests/cacheir/getelem-undefined-null.js b/js/src/jit-test/tests/cacheir/getelem-undefined-null.js
new file mode 100644
index 0000000000..0cf84155cc
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/getelem-undefined-null.js
@@ -0,0 +1,52 @@
+function exists() {
+ var a = {'null': 1, 'undefined': 2};
+ for (var i = 0; i < 100; i++) {
+ assertEq(a[null], 1);
+ assertEq(a[undefined], 2);
+ }
+}
+
+function missing() {
+ var a = {};
+ for (var i = 0; i < 100; i++) {
+ assertEq(a[null], undefined);
+ assertEq(a[undefined], undefined);
+ }
+}
+
+function getter() {
+ var a = {
+ get null() {
+ return 1;
+ },
+ get undefined() {
+ return 2;
+ }
+ }
+ for (var i = 0; i < 100; i++) {
+ assertEq(a[null], 1);
+ assertEq(a[undefined], 2);
+ }
+}
+
+function primitive() {
+ var v = true;
+ for (var i = 0; i < 100; i++) {
+ assertEq(v[null], undefined);
+ assertEq(v[undefined], undefined);
+ }
+}
+
+function mixed() {
+ var a = {'null': 'null', 'undefined': 'undefined'};
+ for (var i = 0; i < 100; i++) {
+ var v = a[i % 2 ? null : undefined]
+ assertEq(a[v], i % 2 ? 'null' : 'undefined');
+ }
+}
+
+exists();
+missing()
+getter();
+primitive();
+mixed();
diff --git a/js/src/jit-test/tests/cacheir/getpropsuper.js b/js/src/jit-test/tests/cacheir/getpropsuper.js
new file mode 100644
index 0000000000..a2eec4646f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/getpropsuper.js
@@ -0,0 +1,15 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+function testGetPropSuper() {
+ class C extends class { static p = 0 } {
+ static m() {
+ return super.p;
+ }
+ }
+
+ for (var i = 0; i < 20; ++i) {
+ var v = C.m();
+ assertEq(v, 0);
+ }
+}
+for (var i = 0; i < 2; ++i) testGetPropSuper();
diff --git a/js/src/jit-test/tests/cacheir/getter-primitive-value.js b/js/src/jit-test/tests/cacheir/getter-primitive-value.js
new file mode 100644
index 0000000000..5dd188d5e1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/getter-primitive-value.js
@@ -0,0 +1,116 @@
+function testNativeGetter() {
+ function test() {
+ var xs = [Symbol("a"), Symbol("b")];
+ var ys = ["a", "b"];
+
+ for (var i = 0; i < 100; ++i) {
+ var r = xs[i & 1].description;
+ assertEq(r, ys[i & 1]);
+ }
+ }
+ for (var i = 0; i < 2; ++i) test();
+}
+testNativeGetter();
+
+function testScriptedGetter() {
+ Object.defineProperty(Symbol.prototype, "desc", {
+ get() {
+ "use strict";
+ assertEq(typeof this, "symbol");
+ return this.description;
+ }
+ });
+
+ function test() {
+ var xs = [Symbol("a"), Symbol("b")];
+ var ys = ["a", "b"];
+
+ for (var i = 0; i < 100; ++i) {
+ var r = xs[i & 1].desc;
+ assertEq(r, ys[i & 1]);
+ }
+ }
+ for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetter();
+
+function testScriptedGetterNonStrict() {
+ Object.defineProperty(Symbol.prototype, "desc_nonstrict", {
+ get() {
+ assertEq(typeof this, "object");
+ return this.description;
+ }
+ });
+
+ function test() {
+ var xs = [Symbol("a"), Symbol("b")];
+ var ys = ["a", "b"];
+
+ for (var i = 0; i < 100; ++i) {
+ var r = xs[i & 1].desc_nonstrict;
+ assertEq(r, ys[i & 1]);
+ }
+ }
+ for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetterNonStrict();
+
+function testNativeGetterPrototype() {
+ Object.defineProperty(Object.prototype, "description_proto",
+ Object.getOwnPropertyDescriptor(Symbol.prototype, "description"));
+
+ function test() {
+ var xs = [Symbol("a"), Symbol("b")];
+ var ys = ["a", "b"];
+
+ for (var i = 0; i < 100; ++i) {
+ var r = xs[i & 1].description_proto;
+ assertEq(r, ys[i & 1]);
+ }
+ }
+ for (var i = 0; i < 2; ++i) test();
+}
+testNativeGetterPrototype();
+
+function testScriptedGetterPrototype() {
+ Object.defineProperty(Object.prototype, "desc_proto", {
+ get() {
+ "use strict";
+ assertEq(typeof this, "symbol");
+ return this.description;
+ }
+ });
+
+ function test() {
+ var xs = [Symbol("a"), Symbol("b")];
+ var ys = ["a", "b"];
+
+ for (var i = 0; i < 100; ++i) {
+ var r = xs[i & 1].desc_proto;
+ assertEq(r, ys[i & 1]);
+ }
+ }
+ for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetterPrototype();
+
+function testScriptedGetterNonStrictPrototype() {
+ Object.defineProperty(Object.prototype, "desc_nonstrict_proto", {
+ get() {
+ assertEq(typeof this, "object");
+ return this.description;
+ }
+ });
+
+ function test() {
+ var xs = [Symbol("a"), Symbol("b")];
+ var ys = ["a", "b"];
+
+ for (var i = 0; i < 100; ++i) {
+ var r = xs[i & 1].desc_nonstrict_proto;
+ assertEq(r, ys[i & 1]);
+ }
+ }
+ for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetterNonStrictPrototype();
diff --git a/js/src/jit-test/tests/cacheir/getter-setter-guards1.js b/js/src/jit-test/tests/cacheir/getter-setter-guards1.js
new file mode 100644
index 0000000000..9ca64dd7da
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/getter-setter-guards1.js
@@ -0,0 +1,64 @@
+// Objects with same shape but different getter/setter.
+function testOwnProp() {
+ var count = 0;
+ var objects = [{get x() { count += 1; }, set x(v) { count += 2; }},
+ {get x() { count += 3; }, set x(v) { count += 4; }},
+ {get x() { count += 5; }, set x(v) { count += 6; }}];
+ assertEq(shapeOf(objects[0]), shapeOf(objects[1]));
+ assertEq(shapeOf(objects[0]), shapeOf(objects[2]));
+ for (var i = 0; i < 150; i++) {
+ var o = objects[i % objects.length];
+ o.x++;
+ }
+ assertEq(count, 1050);
+}
+testOwnProp();
+
+// Ensure optimized TypedArray length properly deoptimizes when the getter is
+// redefined.
+function testTypedArrayLength() {
+ var ta = new Int32Array(10);
+ var lengthHolder = Object.getPrototypeOf(Int32Array.prototype);
+ for (var i = 0; i < 150; i++) {
+ assertEq(ta.length, i <= 100 ? 10 : (i <= 130 ? "foo" : "bar"));
+ assertEq(ta.byteLength, 40);
+ assertEq(ta.byteOffset, 0);
+ if (i === 100) {
+ var desc = Object.getOwnPropertyDescriptor(lengthHolder, "length");
+ desc.get = () => "foo";
+ Object.defineProperty(lengthHolder, "length", desc);
+ }
+ if (i === 130) {
+ var desc = Object.getOwnPropertyDescriptor(lengthHolder, "length");
+ desc.get = () => "bar";
+ Object.defineProperty(lengthHolder, "length", desc);
+ }
+ }
+}
+testTypedArrayLength();
+
+// Native accessors on the global object. Redefine a few times and ensure the
+// right function is called. Use |useWindowProxy: false| to allow optimizing all
+// native getters.
+function testGlobalProp() {
+ var g = newGlobal({useWindowProxy: false});
+ g.evaluate("" + function test() {
+ var arr = [Math, Object];
+ var expected = 0;
+ for (var i = 0; i < 150; i++) {
+ assertEq(timesAccessed, i <= 100 ? i + 1 : (i > 130 ? Infinity : NaN));
+ if (i === 100) {
+ var desc = Object.getOwnPropertyDescriptor(this, "timesAccessed");
+ desc.get = Math.abs;
+ Object.defineProperty(this, "timesAccessed", desc);
+ }
+ if (i === 130) {
+ var desc = Object.getOwnPropertyDescriptor(this, "timesAccessed");
+ desc.get = Math.min;
+ Object.defineProperty(this, "timesAccessed", desc);
+ }
+ }
+ });
+ g.evaluate("test()");
+}
+testGlobalProp();
diff --git a/js/src/jit-test/tests/cacheir/getter-setter-guards2.js b/js/src/jit-test/tests/cacheir/getter-setter-guards2.js
new file mode 100644
index 0000000000..1f6e79cce7
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/getter-setter-guards2.js
@@ -0,0 +1,130 @@
+function testRedefineGetter() {
+ var callGetter = function(o) {
+ return o.x;
+ };
+
+ var proto = {get foo() {}, bar: 1};
+ var obj = Object.create(proto);
+
+ // Add "x" getter on the prototype. Warm up the IC.
+ var count1 = 0;
+ Object.defineProperty(proto, "x", {get: function(v) {
+ count1++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callGetter(obj);
+ }
+ assertEq(count1, 20);
+
+ // Redefine "x" with a different getter. Ensure the new getter is called.
+ var count2 = 0;
+ Object.defineProperty(proto, "x", {get: function() {
+ count2++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callGetter(obj);
+ }
+ assertEq(count1, 20);
+ assertEq(count2, 20);
+}
+testRedefineGetter();
+
+function testRedefineSetter() {
+ var callSetter = function(o) {
+ o.x = 1;
+ };
+
+ var proto = {get foo() {}, bar: 1};
+ var obj = Object.create(proto);
+
+ // Add "x" setter on the prototype. Warm up the IC.
+ var count1 = 0;
+ Object.defineProperty(proto, "x", {set: function(v) {
+ count1++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callSetter(obj);
+ }
+ assertEq(count1, 20);
+
+ // Redefine "x" with a different setter. Ensure the new setter is called.
+ var count2 = 0;
+ Object.defineProperty(proto, "x", {set: function() {
+ count2++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callSetter(obj);
+ }
+ assertEq(count1, 20);
+ assertEq(count2, 20);
+}
+testRedefineSetter();
+
+function testDeleteAdd() {
+ var callGetter = function(o) {
+ return o.x;
+ };
+
+ var proto = {get foo() {}, bar: 1};
+ var obj = Object.create(proto);
+
+ // Add "x" getter on the prototype. Warm up the IC.
+ var count1 = 0;
+ Object.defineProperty(proto, "x", {get: function() {
+ count1++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callGetter(obj);
+ }
+ assertEq(count1, 20);
+
+ // Delete the getter.
+ delete proto.x;
+
+ // Add "x" back with a different getter. Ensure the new getter is called.
+ var count2 = 0;
+ Object.defineProperty(proto, "x", {get: function() {
+ count2++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callGetter(obj);
+ }
+ assertEq(count1, 20);
+ assertEq(count2, 20);
+}
+testDeleteAdd();
+
+function testAccessorToDataAndBack() {
+ var callGetter = function(o) {
+ return o.x;
+ };
+
+ var proto = {get foo() {}, bar: 1};
+ var obj = Object.create(proto);
+
+ // Add "x" getter on the prototype. Warm up the IC.
+ var count1 = 0;
+ Object.defineProperty(proto, "x", {get: function() {
+ count1++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callGetter(obj);
+ }
+ assertEq(count1, 20);
+
+ // Turn the getter into a data property.
+ Object.defineProperty(proto, "x", {configurable: true, value: 123});
+
+ // Turn the data property into a (different) getter. Ensure the new getter is
+ // called.
+ var count2 = 0;
+ Object.defineProperty(proto, "x", {get: function() {
+ count2++;
+ }, configurable: true});
+ for (var i = 0; i < 20; i++) {
+ callGetter(obj);
+ }
+ assertEq(count1, 20);
+ assertEq(count2, 20);
+}
+testAccessorToDataAndBack();
diff --git a/js/src/jit-test/tests/cacheir/global-getter.js b/js/src/jit-test/tests/cacheir/global-getter.js
new file mode 100644
index 0000000000..cd8d0b22df
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/global-getter.js
@@ -0,0 +1,36 @@
+// Tests for |this| value passed to getters defined on the global.
+
+function test(useWindowProxy) {
+ var g = newGlobal({useWindowProxy});
+ g.useWindowProxy = useWindowProxy;
+ g.evaluate(`
+ var x = 123;
+ Object.defineProperty(this, "getX", {get: function() { return this.x; }});
+ Object.defineProperty(Object.prototype, "protoGetX", {get: function() { return this.x * 2; }});
+ Object.defineProperty(this, "thisIsProxy", {get: function() { return isProxy(this); }});
+
+ function f() {
+ for (var i = 0; i < 100; i++) {
+ // GetGName
+ assertEq(getX, 123);
+ assertEq(protoGetX, 246);
+ assertEq(thisIsProxy, useWindowProxy);
+ // GetProp
+ assertEq(globalThis.getX, 123);
+ assertEq(globalThis.protoGetX, 246);
+ assertEq(globalThis.thisIsProxy, useWindowProxy);
+ }
+ }
+ f();
+ `);
+}
+
+for (let useWindowProxy of [true, false]) {
+ test(useWindowProxy);
+}
+
+setJitCompilerOption("ic.force-megamorphic", 1);
+
+for (let useWindowProxy of [true, false]) {
+ test(useWindowProxy);
+}
diff --git a/js/src/jit-test/tests/cacheir/global-setter.js b/js/src/jit-test/tests/cacheir/global-setter.js
new file mode 100644
index 0000000000..19703104ee
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/global-setter.js
@@ -0,0 +1,39 @@
+// Tests for |this| value passed to setters defined on the global.
+
+function test(useWindowProxy) {
+ var g = newGlobal({useWindowProxy});
+ g.useWindowProxy = useWindowProxy;
+ g.evaluate(`
+ var x = 123;
+ Object.defineProperty(this, "checkX",
+ {set: function(v) { assertEq(this.x, v); }});
+ Object.defineProperty(Object.prototype, "protoCheckX",
+ {set: function(v) { assertEq(this.x * 2, v); }});
+ Object.defineProperty(this, "checkThisIsProxy",
+ {set: function(v) { assertEq(isProxy(this), v); }});
+
+ function f() {
+ for (var i = 0; i < 100; i++) {
+ // SetGName
+ checkX = 123;
+ protoCheckX = 246;
+ checkThisIsProxy = useWindowProxy;
+ // SetProp
+ globalThis.checkX = 123;
+ globalThis.protoCheckX = 246;
+ globalThis.checkThisIsProxy = useWindowProxy;
+ }
+ }
+ f();
+ `);
+}
+
+for (let useWindowProxy of [true, false]) {
+ test(useWindowProxy);
+}
+
+setJitCompilerOption("ic.force-megamorphic", 1);
+
+for (let useWindowProxy of [true, false]) {
+ test(useWindowProxy);
+}
diff --git a/js/src/jit-test/tests/cacheir/has-sparse.js b/js/src/jit-test/tests/cacheir/has-sparse.js
new file mode 100644
index 0000000000..2b3b2c8451
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/has-sparse.js
@@ -0,0 +1,58 @@
+function sparse() {
+ var o = {0: 0, 0x10000: 0};
+
+ var tests = [[1, false], [0, true], [-2, false], [0x10000, true], [0x20000, false]];
+ for (var [key, has] of tests) {
+ assertEq(key in o, has);
+ assertEq(o.hasOwnProperty(key), has);
+ }
+}
+
+function typedArray() {
+ var o = {0: 0, 0x10000: 0};
+ var t = new Int32Array(0x10001)
+
+ // Only use Int32Array after we attached the sparse stub
+ // in o, in t
+ var tests = [[1, [false, true]],
+ [0, [true, true]],
+ [-2, [false, false]],
+ [0x10000, [true, true]],
+ [0x20000, [false, false]]];
+
+ for (var i = 0; i < 10; i++) {
+ for (var [key, has] of tests) {
+ assertEq(key in o, has[i > 5 ? 1 : 0]);
+ assertEq(o.hasOwnProperty(key), has[i > 5 ? 1 : 0]);
+ }
+
+ if (i == 5)
+ o = t;
+ }
+}
+
+function protoChange() {
+ var o = {0: 0, 0x10000: 0};
+
+ var tests = [[1, [false, true]],
+ [0, [true, true]],
+ [-2, [false, false]],
+ [0x10000, [true, true]],
+ [0x20000, [false, false]]];
+
+ for (var i = 0; i < 10; i++) {
+ for (var [key, has] of tests) {
+ assertEq(key in o, has[i > 5 ? 1 : 0]);
+ // Proto change doesn't affect hasOwnProperty.
+ assertEq(o.hasOwnProperty(key), has[0]);
+ }
+
+ if (i == 5)
+ o.__proto__ = [1, 1, 1, 1];
+ }
+}
+
+sparse();
+typedArray();
+protoChange();
+
diff --git a/js/src/jit-test/tests/cacheir/has-undefined-null.js b/js/src/jit-test/tests/cacheir/has-undefined-null.js
new file mode 100644
index 0000000000..7f0109a5c1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/has-undefined-null.js
@@ -0,0 +1,28 @@
+function exists() {
+ var a = {'null': 0, 'undefined': 0};
+ for (var i = 0; i < 100; i++) {
+ assertEq(null in a, true);
+ assertEq(undefined in a, true);
+ }
+}
+
+function missing() {
+ var a = {};
+ for (var i = 0; i < 100; i++) {
+ assertEq(null in a, false);
+ assertEq(undefined in a, false);
+ }
+}
+
+function mixed() {
+ var x = [{'null': 0}, {'undefined': 0}]
+ for (var i = 0; i < 100; i++) {
+ var a = x[i % 2];
+ assertEq(null in a, i % 2 == 0);
+ assertEq(undefined in a, i % 2 == 1);
+ }
+}
+
+exists();
+missing();
+mixed();
diff --git a/js/src/jit-test/tests/cacheir/has.js b/js/src/jit-test/tests/cacheir/has.js
new file mode 100644
index 0000000000..9dba6330d8
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/has.js
@@ -0,0 +1,66 @@
+var max = 40;
+var key = "d";
+setJitCompilerOption("ion.warmup.trigger", max - 10);
+
+function simple() {
+ var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}];
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, false);
+ }
+}
+
+function megamorphic() {
+ var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1},
+ {a: 1, b: 1}, {c: 1, e: 1, a: 1},
+ {__proto__:{e: 1, f: 1, a: 1, g: 1}},
+ {__proto__:{e: 1, f: 1, a: 1, g: 1, h: 1}}];
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, false);
+ }
+}
+
+function protoSetProp() {
+ var base = {a: 1};
+ var array = [{__proto__: base},
+ {__proto__: base, b: 1, a: 1},
+ {__proto__: base, c: 1, a: 1}];
+ for (var j = 0; j < 2; j++) {
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, (j > 0));
+ }
+ base.d = 1; // Define property on prototype
+ }
+}
+
+function protoSetElem() {
+ var base = {a: 1};
+ var array = [{__proto__: base},
+ {__proto__: base, b: 1, a: 1},
+ {__proto__: base, c: 1, a: 1}];
+ for (var j = 0; j < 2; j++) {
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq("a" in x, true);
+ assertEq("d" in x, (j > 0));
+ }
+ base[key] = 1; // Define property on prototype
+ }
+}
+
+function test() {
+ for (var i = 0; i < max; i++) {
+ simple();
+ megamorphic();
+ protoSetProp();
+ protoSetElem();
+ }
+}
+
+test();
+test();
diff --git a/js/src/jit-test/tests/cacheir/hasown.js b/js/src/jit-test/tests/cacheir/hasown.js
new file mode 100644
index 0000000000..5a3bf28d6d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/hasown.js
@@ -0,0 +1,46 @@
+var max = 40;
+setJitCompilerOption("ion.warmup.trigger", max - 10);
+
+function simple() {
+ var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1}];
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq(x.hasOwnProperty("a"), true);
+ assertEq(x.hasOwnProperty("d"), false);
+ }
+}
+
+function megamorphic() {
+ var array = [{a: 1}, {b: 1, a: 1}, {c: 1, a: 1},
+ {a: 1, b: 1}, {c: 1, e: 1, a: 1},
+ {e: 1, f: 1, a: 1, g: 1},
+ {e: 1, f: 1, a: 1, g: 1, h: 1}];
+ for (var i = 0; i < array.length; i++) {
+ var x = array[i];
+ assertEq(x.hasOwnProperty("a"), true);
+ assertEq(x.hasOwnProperty("d"), false);
+ }
+}
+
+function key() {
+ var sym = Symbol(), sym2 = Symbol();
+ var keys = [[sym, true], [sym2, false],
+ ["a", true], ["b", false],
+ [{}, false]];
+ var obj = {[sym]: 1, a: 1};
+ for (var i = 0; i < keys.length; i++) {
+ var [key, result] = keys[i];
+ assertEq(obj.hasOwnProperty(key), result);
+ }
+}
+
+function test() {
+ for (var i = 0; i < max; i++) {
+ simple();
+ megamorphic();
+ key();
+ }
+}
+
+test();
+test();
diff --git a/js/src/jit-test/tests/cacheir/iter-megamorphic.js b/js/src/jit-test/tests/cacheir/iter-megamorphic.js
new file mode 100644
index 0000000000..8fcfe7dcd5
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/iter-megamorphic.js
@@ -0,0 +1,18 @@
+function testIter(v) {
+ var c = 0;
+ for (var p in v) {
+ c++;
+ }
+ assertEq(c === 0 || c === 1, true);
+ assertEq(c === 0, v === null || v === undefined);
+}
+function test() {
+ var vals = [{a: 1}, {b: 1}, {c: 1}, {d: 1}, null, undefined,
+ {e: 1}, {f: 1}, {g: 1}, {h: 1}, {i: 1}];
+ for (var i = 0; i < 100; i++) {
+ for (var v of vals) {
+ testIter(v);
+ }
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/cacheir/load-typed-element-bigint.js b/js/src/jit-test/tests/cacheir/load-typed-element-bigint.js
new file mode 100644
index 0000000000..926cd9026e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/load-typed-element-bigint.js
@@ -0,0 +1,100 @@
+// Different typed array types to ensure we emit a GetProp IC.
+var xs = [
+ new BigInt64Array(10),
+ new BigUint64Array(10),
+];
+
+// Load 0n value.
+function loadConstantZero() {
+ var value = 0n;
+
+ xs[0][0] = value;
+ xs[1][0] = value;
+
+ var ys = [
+ BigInt.asIntN(64, value),
+ BigInt.asUintN(64, value),
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ assertEq(ta[0], ys[i & 1]);
+ }
+}
+loadConstantZero();
+
+// Load non-negative BigInt using inline digits.
+function loadInlineDigits() {
+ var value = 1n;
+
+ xs[0][0] = value;
+ xs[1][0] = value;
+
+ var ys = [
+ BigInt.asIntN(64, value),
+ BigInt.asUintN(64, value),
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ assertEq(ta[0], ys[i & 1]);
+ }
+}
+loadInlineDigits();
+
+// Load negative BigInt using inline digits.
+function loadInlineDigitsNegative() {
+ var value = -1n;
+
+ xs[0][0] = value;
+ xs[1][0] = value;
+
+ var ys = [
+ BigInt.asIntN(64, value),
+ BigInt.asUintN(64, value),
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ assertEq(ta[0], ys[i & 1]);
+ }
+}
+loadInlineDigitsNegative();
+
+// Still inline digits, but now two digits on 32-bit platforms
+function loadInlineDigitsTwoDigits() {
+ var value = 4294967296n;
+
+ xs[0][0] = value;
+ xs[1][0] = value;
+
+ var ys = [
+ BigInt.asIntN(64, value),
+ BigInt.asUintN(64, value),
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ assertEq(ta[0], ys[i & 1]);
+ }
+}
+loadInlineDigitsTwoDigits();
+
+// Negative case of |storeInlineDigitsTwoDigits|.
+function loadInlineDigitsTwoDigitsNegative() {
+ var value = -4294967296n;
+
+ xs[0][0] = value;
+ xs[1][0] = value;
+
+ var ys = [
+ BigInt.asIntN(64, value),
+ BigInt.asUintN(64, value),
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ assertEq(ta[0], ys[i & 1]);
+ }
+}
+loadInlineDigitsTwoDigitsNegative();
diff --git a/js/src/jit-test/tests/cacheir/map-get-bigint.js b/js/src/jit-test/tests/cacheir/map-get-bigint.js
new file mode 100644
index 0000000000..5ddbacec15
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-get-bigint.js
@@ -0,0 +1,88 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i + 1]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testInlineDigitsSameSign(n) {
+ var xs = [1n, 2n];
+ var ys = [3n, 4n];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testInlineDigitsSameSign);
+
+function testInlineDigitsDifferentSign(n) {
+ var xs = [-1n, 2n];
+ var ys = [1n, -2n];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testInlineDigitsDifferentSign);
+
+function testHeapDigitsSameSign(n) {
+ // Definitely uses heap digits.
+ var heap = 2n ** 1000n;
+
+ var xs = [heap + 1n, heap + 2n];
+ var ys = [heap + 3n, heap + 4n];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testHeapDigitsSameSign);
+
+function testHeapDigitsDifferentSign(n) {
+ // Definitely uses heap digits.
+ var heap = 2n ** 1000n;
+
+ var xs = [-(heap + 1n), heap + 2n];
+ var ys = [heap + 1n, -(heap + 2n)];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testHeapDigitsDifferentSign);
diff --git a/js/src/jit-test/tests/cacheir/map-get-nongcthing.js b/js/src/jit-test/tests/cacheir/map-get-nongcthing.js
new file mode 100644
index 0000000000..9e36c6b871
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-get-nongcthing.js
@@ -0,0 +1,116 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i + 1]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testInt32(n) {
+ var xs = [1, 2];
+ var ys = [3, 4];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testInt32);
+
+function testDouble(n) {
+ var xs = [Math.PI, Infinity];
+ var ys = [Math.E, -Infinity];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testDouble);
+
+function testZero(n) {
+ var xs = [0, -0];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap([0], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testZero);
+
+function testNaN(n) {
+ var xs = [NaN, -NaN];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap([NaN], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testNaN);
+
+function testUndefinedAndNull(n) {
+ var xs = [undefined, null];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testUndefinedAndNull);
+
+function testBoolean(n) {
+ var xs = [true, false];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testBoolean);
diff --git a/js/src/jit-test/tests/cacheir/map-get-object.js b/js/src/jit-test/tests/cacheir/map-get-object.js
new file mode 100644
index 0000000000..076c28a7b5
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-get-object.js
@@ -0,0 +1,31 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i + 1]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function test(n) {
+ var xs = [{}, {}];
+ var ys = [{}, {}];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(test);
diff --git a/js/src/jit-test/tests/cacheir/map-get-string.js b/js/src/jit-test/tests/cacheir/map-get-string.js
new file mode 100644
index 0000000000..1a25d09fd3
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-get-string.js
@@ -0,0 +1,83 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i + 1]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testConstant(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testConstant);
+
+function testConstantFatInline(n) {
+ var xs = ["a", "b"].map(s => s.repeat(10));
+ var ys = ["c", "d"].map(s => s.repeat(10));
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testConstantFatInline);
+
+function testComputed(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ z = String.fromCharCode(z.charCodeAt(0));
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testComputed);
+
+function testRope(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs.map(x => x.repeat(100)), n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3].repeat(100);
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(testRope);
diff --git a/js/src/jit-test/tests/cacheir/map-get-symbol.js b/js/src/jit-test/tests/cacheir/map-get-symbol.js
new file mode 100644
index 0000000000..face9e4c2b
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-get-symbol.js
@@ -0,0 +1,31 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i + 1]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function test(n) {
+ var xs = [Symbol(), Symbol()];
+ var ys = [Symbol(), Symbol()];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, N / 2 + N / 4);
+}
+runTest(test);
diff --git a/js/src/jit-test/tests/cacheir/map-get-value.js b/js/src/jit-test/tests/cacheir/map-get-value.js
new file mode 100644
index 0000000000..0b5e288a06
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-get-value.js
@@ -0,0 +1,31 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i + 1]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testPolymorphic(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 15];
+ var v = map.get(z);
+ if (v !== undefined) c += v;
+ }
+ assertEq(c, (8 * 9) / 2 * 8);
+}
+runTest(testPolymorphic);
diff --git a/js/src/jit-test/tests/cacheir/map-has-bigint.js b/js/src/jit-test/tests/cacheir/map-has-bigint.js
new file mode 100644
index 0000000000..5ae51a093f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-has-bigint.js
@@ -0,0 +1,84 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testInlineDigitsSameSign(n) {
+ var xs = [1n, 2n];
+ var ys = [3n, 4n];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testInlineDigitsSameSign);
+
+function testInlineDigitsDifferentSign(n) {
+ var xs = [-1n, 2n];
+ var ys = [1n, -2n];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testInlineDigitsDifferentSign);
+
+function testHeapDigitsSameSign(n) {
+ // Definitely uses heap digits.
+ var heap = 2n ** 1000n;
+
+ var xs = [heap + 1n, heap + 2n];
+ var ys = [heap + 3n, heap + 4n];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testHeapDigitsSameSign);
+
+function testHeapDigitsDifferentSign(n) {
+ // Definitely uses heap digits.
+ var heap = 2n ** 1000n;
+
+ var xs = [-(heap + 1n), heap + 2n];
+ var ys = [heap + 1n, -(heap + 2n)];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testHeapDigitsDifferentSign);
diff --git a/js/src/jit-test/tests/cacheir/map-has-nongcthing.js b/js/src/jit-test/tests/cacheir/map-has-nongcthing.js
new file mode 100644
index 0000000000..8fc653564f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-has-nongcthing.js
@@ -0,0 +1,110 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testInt32(n) {
+ var xs = [1, 2];
+ var ys = [3, 4];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testInt32);
+
+function testDouble(n) {
+ var xs = [Math.PI, Infinity];
+ var ys = [Math.E, -Infinity];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testDouble);
+
+function testZero(n) {
+ var xs = [0, -0];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap([0], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testZero);
+
+function testNaN(n) {
+ var xs = [NaN, -NaN];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap([NaN], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testNaN);
+
+function testUndefinedAndNull(n) {
+ var xs = [undefined, null];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testUndefinedAndNull);
+
+function testBoolean(n) {
+ var xs = [true, false];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testBoolean);
diff --git a/js/src/jit-test/tests/cacheir/map-has-object.js b/js/src/jit-test/tests/cacheir/map-has-object.js
new file mode 100644
index 0000000000..46366586da
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-has-object.js
@@ -0,0 +1,30 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function test(n) {
+ var xs = [{}, {}];
+ var ys = [{}, {}];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(test);
diff --git a/js/src/jit-test/tests/cacheir/map-has-string.js b/js/src/jit-test/tests/cacheir/map-has-string.js
new file mode 100644
index 0000000000..84a139a9e2
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-has-string.js
@@ -0,0 +1,79 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testConstant(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testConstant);
+
+function testConstantFatInline(n) {
+ var xs = ["a", "b"].map(s => s.repeat(10));
+ var ys = ["c", "d"].map(s => s.repeat(10));
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testConstantFatInline);
+
+function testComputed(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ z = String.fromCharCode(z.charCodeAt(0));
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testComputed);
+
+function testRope(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs.map(x => x.repeat(100)), n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3].repeat(100);
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testRope);
diff --git a/js/src/jit-test/tests/cacheir/map-has-symbol.js b/js/src/jit-test/tests/cacheir/map-has-symbol.js
new file mode 100644
index 0000000000..c0268a3da1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-has-symbol.js
@@ -0,0 +1,30 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function test(n) {
+ var xs = [Symbol(), Symbol()];
+ var ys = [Symbol(), Symbol()];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(test);
diff --git a/js/src/jit-test/tests/cacheir/map-has-value.js b/js/src/jit-test/tests/cacheir/map-has-value.js
new file mode 100644
index 0000000000..fddb1e8b5d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-has-value.js
@@ -0,0 +1,30 @@
+// Return a new map, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createMap(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Map(xs.map((x, i) => [x, i]));
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testPolymorphic(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []];
+ var zs = [...xs, ...ys];
+ var map = createMap(xs, n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 15];
+ if (map.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testPolymorphic);
diff --git a/js/src/jit-test/tests/cacheir/map-size.js b/js/src/jit-test/tests/cacheir/map-size.js
new file mode 100644
index 0000000000..55c8bb3e82
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/map-size.js
@@ -0,0 +1,43 @@
+function testEmpty() {
+ let map = new Map();
+ for (let i = 0; i < 100; ++i) {
+ assertEq(map.size, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) testEmpty();
+
+function testSimple() {
+ let map = new Map([1, 2, 3, 4].entries());
+ for (let i = 0; i < 100; ++i) {
+ assertEq(map.size, 4);
+ }
+}
+for (let i = 0; i < 2; ++i) testSimple();
+
+function testWithDelete() {
+ for (let i = 0; i < 100; ++i) {
+ let a = [1, 2, 3, 4];
+ let map = new Map(a.entries());
+ for (let j = 0; j < a.length; ++j) {
+ assertEq(map.size, a.length - j);
+ map.delete(j);
+ assertEq(map.size, a.length - j - 1);
+ }
+ assertEq(map.size, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) testWithDelete();
+
+function testWithSet() {
+ for (let i = 0; i < 100; ++i) {
+ let a = [1, 2, 3, 4];
+ let map = new Map();
+ for (let j = 0; j < a.length; ++j) {
+ assertEq(map.size, j);
+ map.set(a[j], 0);
+ assertEq(map.size, j + 1);
+ }
+ assertEq(map.size, a.length);
+ }
+}
+for (let i = 0; i < 2; ++i) testWithSet();
diff --git a/js/src/jit-test/tests/cacheir/math-min-max.js b/js/src/jit-test/tests/cacheir/math-min-max.js
new file mode 100644
index 0000000000..b7b9fbe86a
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/math-min-max.js
@@ -0,0 +1,180 @@
+function test1ArgInt32() {
+ function test(x, expected) {
+ assertEq(Math.max(x), expected);
+ assertEq(Math.min(x), expected);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(i, i);
+ }
+ // Fail Int32 guard.
+ test(true, 1);
+ test({}, NaN);
+}
+test1ArgInt32();
+
+function test1ArgNumber() {
+ function test(x, expected) {
+ assertEq(Math.max(x), expected);
+ assertEq(Math.min(x), expected);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(3.14, 3.14);
+ test(-0, -0);
+ test(i, i);
+ }
+ // Fail Number guard.
+ test(true, 1);
+ test({}, NaN);
+}
+test1ArgNumber();
+
+function test1ArgInt32ThenNumber() {
+ function test(x, expected) {
+ assertEq(Math.max(x), expected);
+ assertEq(Math.min(x), expected);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(i, i);
+ }
+ for (var i = 0; i < 10; i++) {
+ test(i * 3.14, i * 3.14);
+ }
+}
+test1ArgInt32ThenNumber();
+
+function test2ArgsInt32() {
+ function test(x, y, expectedMax, expectedMin) {
+ assertEq(Math.max(x, y), expectedMax);
+ assertEq(Math.min(x, y), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(0, i, i, 0);
+ test(-9, -1, -1, -9);
+ test(0, 0, 0, 0);
+ }
+ // Fail Int32 guard.
+ test(0, "3", 3, 0);
+ test({}, 2, NaN, NaN);
+}
+test2ArgsInt32();
+
+function test2ArgsNumber() {
+ function test(x, y, expectedMax, expectedMin) {
+ assertEq(Math.max(x, y), expectedMax);
+ assertEq(Math.min(x, y), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(1.1, 2.2, 2.2, 1.1);
+ test(8, NaN, NaN, NaN);
+ test(NaN, 8, NaN, NaN);
+ test(-0, i, i, -0);
+ test(Infinity, -0, Infinity, -0);
+ test(-Infinity, Infinity, Infinity, -Infinity);
+ }
+ // Fail Number guard.
+ test(-0, "3", 3, -0);
+ test({}, 2.1, NaN, NaN);
+}
+test2ArgsNumber();
+
+function test2ArgsInt32ThenNumber() {
+ function test(x, y, expectedMax, expectedMin) {
+ assertEq(Math.max(x, y), expectedMax);
+ assertEq(Math.min(x, y), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(-1, i, i, -1);
+ }
+ for (var i = 0; i < 10; i++) {
+ test(-0, i, i, -0);
+ }
+}
+test2ArgsInt32ThenNumber();
+
+function test3ArgsInt32() {
+ function test(a, b, c, expectedMax, expectedMin) {
+ assertEq(Math.max(a, b, c), expectedMax);
+ assertEq(Math.min(a, b, c), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(30, 100, i, 100, i);
+ test(i, 0, -2, i, -2);
+ }
+ // Fail Int32 guard.
+ test(0, 1, "2", 2, 0);
+ test(-0, 1, 2, 2, -0);
+}
+test3ArgsInt32();
+
+function test3ArgsNumber() {
+ function test(a, b, c, expectedMax, expectedMin) {
+ assertEq(Math.max(a, b, c), expectedMax);
+ assertEq(Math.min(a, b, c), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(100, i, -0, 100, -0);
+ test(i, NaN, -1, NaN, NaN);
+ }
+ // Fail Number guard.
+ test(-0, "3", 1, 3, -0);
+ test("9", 1.1, 3, 9, 1.1);
+}
+test3ArgsNumber();
+
+function test3ArgsInt32ThenNumber() {
+ function test(a, b, c, expectedMax, expectedMin) {
+ assertEq(Math.max(a, b, c), expectedMax);
+ assertEq(Math.min(a, b, c), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(30, 100, i, 100, i);
+ }
+ for (var i = 0; i < 10; i++) {
+ test(123.4, 100, i, 123.4, i);
+ }
+}
+test3ArgsInt32ThenNumber();
+
+function test4ArgsInt32() {
+ function test(a, b, c, d, expectedMax, expectedMin) {
+ assertEq(Math.max(a, b, c, d), expectedMax);
+ assertEq(Math.min(a, b, c, d), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(30, 100, i, 0, 100, 0);
+ test(i, 0, -1, -2, i, -2);
+ }
+ // Fail Int32 guard.
+ test(0, 1, 2, "3", 3, 0);
+ test(-0, 1, 2, 3, 3, -0);
+}
+test4ArgsInt32();
+
+function test4ArgsNumber() {
+ function test(a, b, c, d, expectedMax, expectedMin) {
+ assertEq(Math.max(a, b, c, d), expectedMax);
+ assertEq(Math.min(a, b, c, d), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(3.1, 100, i, -0, 100, -0);
+ test(i, NaN, -1, -2, NaN, NaN);
+ }
+ // Fail Number guard.
+ test(-0, 1, 2, "3", 3, -0);
+ test("9", 1.1, 2, 3, 9, 1.1);
+}
+test4ArgsNumber();
+
+function test4ArgsInt32ThenNumber() {
+ function test(a, b, c, d, expectedMax, expectedMin) {
+ assertEq(Math.max(a, b, c, d), expectedMax);
+ assertEq(Math.min(a, b, c, d), expectedMin);
+ }
+ for (var i = 0; i < 20; i++) {
+ test(i << 1, i - 100, -1, -2, i * 2, i - 100);
+ }
+ for (var i = 0; i < 10; i++) {
+ test(i * 1.1, i, -0, 0, i * 1.1, -0);
+ }
+}
+test4ArgsInt32ThenNumber();
diff --git a/js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js b/js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js
new file mode 100644
index 0000000000..b6098435a1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/megamorphic-get-has-dense.js
@@ -0,0 +1,63 @@
+setJitCompilerOption("ic.force-megamorphic", 1);
+
+function testBasic() {
+ // Create various native objects with dense elements.
+ var objs = [[1, 2, 3], {0: 1, 1: 2, 2: 3}];
+ var fun = x => x;
+ fun[0] = 1;
+ fun[1] = 2;
+ fun[2] = 3;
+ objs.push(fun);
+ for (var i = 0; i < 20; i++) {
+ var o = {};
+ o["x" + i] = i;
+ o[0] = 1;
+ o[1] = 2;
+ if (i < 10) {
+ o[2] = 3;
+ } else {
+ // o[2] will be a hole.
+ o[3] = 4;
+ }
+ objs.push(o);
+ }
+
+ // Access their dense elements.
+ for (var i = 0; i < 10; i++) {
+ for (var j = 0; j < objs.length; j++) {
+ var obj = objs[j];
+ assertEq(obj[0], 1);
+ assertEq(obj[1], 2);
+ assertEq(obj[2], j < 13 ? 3 : undefined);
+ assertEq(obj[3], j >= 13 ? 4 : undefined);
+ assertEq(0 in obj, true);
+ assertEq(1 in obj, true);
+ assertEq(2 in obj, j < 13);
+ assertEq(3 in obj, j >= 13);
+ assertEq(Object.hasOwn(obj, 0), true);
+ assertEq(Object.hasOwn(obj, 1), true);
+ assertEq(Object.hasOwn(obj, 2), j < 13);
+ assertEq(Object.hasOwn(obj, 3), j >= 13);
+ }
+ }
+}
+testBasic();
+
+function testNonNative() {
+ var arr = [1, 2, 3];
+ var proxy = new Proxy({}, {
+ get(target, prop) { return 456; },
+ has(target, prop) { return prop === "0"; },
+ });
+ for (var i = 0; i < 100; i++) {
+ var obj = i < 95 ? arr : proxy;
+ assertEq(obj[0], i < 95 ? 1 : 456);
+ assertEq(0 in obj, true);
+ assertEq(1 in obj, i < 95);
+ assertEq(4 in obj, false);
+ assertEq(Object.hasOwn(obj, 0), i < 95);
+ assertEq(Object.hasOwn(obj, 1), i < 95);
+ assertEq(Object.hasOwn(obj, 4), false);
+ }
+}
+testNonNative();
diff --git a/js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js
new file mode 100644
index 0000000000..7ecbbe36c4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype-failure.js
@@ -0,0 +1,38 @@
+// Test failure when constructing a function whose |.prototype| property isn't an object.
+
+function Klass() {
+ this.prop = 1;
+}
+
+// Save the original prototype.
+const KlassPrototype = Klass.prototype;
+
+// Set the prototype to a non-object value.
+Klass.prototype = null;
+
+const prototypes = [
+ null,
+ KlassPrototype,
+];
+
+const N = 500;
+let c = 0;
+
+for (let i = 0; i <= N; ++i) {
+ // Always perform a set to avoid a cold-code bailout.
+ let proto = prototypes[(i === N)|0];
+ Klass.prototype = proto;
+
+ // Create a new object.
+ let o = new Klass();
+
+ // Read a property from the new object to ensure it was correctly allocated
+ // and initialised.
+ c += o.prop;
+
+ // The prototype defaults to %Object.prototype% when the |.prototype|
+ // property isn't an object.
+ assertEq(Object.getPrototypeOf(o), proto === null ? Object.prototype : KlassPrototype);
+}
+
+assertEq(c, N + 1);
diff --git a/js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js
new file mode 100644
index 0000000000..5e146db4b5
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/new-with-non-object-prototype.js
@@ -0,0 +1,129 @@
+// Test constructing a function when the |.prototype| property isn't an object.
+
+function test(proto) {
+ function Klass() {
+ this.prop = 1;
+ }
+ Klass.prototype = proto;
+
+ const N = 100;
+
+ let c = 0;
+ for (let i = 0; i < N; ++i) {
+ // Create a new object.
+ let o = new Klass();
+
+ // Read a property from the new object to ensure it was correctly allocated
+ // and initialised.
+ c += o.prop;
+
+ // The prototype defaults to %Object.prototype% when the |.prototype|
+ // property isn't an object.
+ assertEq(Object.getPrototypeOf(o), Object.prototype);
+ }
+
+ assertEq(c, N);
+}
+
+const primitivesTypes = [
+ undefined,
+ null,
+ 123,
+ true,
+ "str",
+ Symbol(),
+ 123n,
+];
+
+for (let primitive of primitivesTypes) {
+ // Create a fresh function object to avoid type pollution.
+ let fn = Function(`return ${test}`)();
+
+ fn(primitive);
+}
+
+// Repeat the test from above, but this time |Klass| is a cross-realm function.
+
+function testCrossRealm(proto) {
+ const otherGlobal = newGlobal();
+ const Klass = otherGlobal.eval(`
+ function Klass() {
+ this.prop = 1;
+ }
+ Klass;
+ `);
+ Klass.prototype = proto;
+
+ const N = 100;
+
+ let c = 0;
+ for (let i = 0; i < N; ++i) {
+ // Create a new object.
+ let o = new Klass();
+
+ // Read a property from the new object to ensure it was correctly allocated
+ // and initialised.
+ c += o.prop;
+
+ // The prototype defaults to %Object.prototype% when the |.prototype|
+ // property isn't an object.
+ assertEq(Object.getPrototypeOf(o), otherGlobal.Object.prototype);
+ }
+
+ assertEq(c, N);
+}
+
+for (let primitive of primitivesTypes) {
+ // Create a fresh function object to avoid type pollution.
+ let fn = Function(`return ${testCrossRealm}`)();
+
+ fn(primitive);
+}
+
+// Repeat the test from above, but this time |Klass| is a cross-realm new.target.
+
+function testCrossRealmNewTarget(proto) {
+ const otherGlobal = newGlobal();
+ const Klass = otherGlobal.eval(`
+ function Klass() {}
+ Klass;
+ `);
+ Klass.prototype = proto;
+
+ class C {
+ constructor() {
+ this.prop = 1;
+ }
+ }
+
+ class D extends C {
+ constructor() {
+ super();
+ }
+ }
+
+ const N = 100;
+
+ let c = 0;
+ for (let i = 0; i < N; ++i) {
+ // Create a new object.
+ let o = Reflect.construct(D, [], Klass);
+
+ // Read a property from the new object to ensure it was correctly allocated
+ // and initialised.
+ c += o.prop;
+
+ // The prototype defaults to %Object.prototype% when the |.prototype|
+ // property isn't an object.
+ assertEq(Object.getPrototypeOf(o), otherGlobal.Object.prototype);
+ }
+
+ assertEq(c, N);
+}
+
+for (let primitive of primitivesTypes) {
+ // Create a fresh function object to avoid type pollution.
+ let fn = Function(`return ${testCrossRealmNewTarget}`)();
+
+ fn(primitive);
+}
diff --git a/js/src/jit-test/tests/cacheir/nukedCCW.js b/js/src/jit-test/tests/cacheir/nukedCCW.js
new file mode 100644
index 0000000000..53a14c067f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/nukedCCW.js
@@ -0,0 +1,40 @@
+function testNuke() {
+ var wrapper = evaluate("({a: 15, b: {c: 42}})",
+ {global: newGlobal({newCompartment: true, sameZoneAs: this})});
+
+ var i, error;
+ try {
+ for (i = 0; i < 150; i++) {
+ assertEq(wrapper.b.c, 42);
+ assertEq(wrapper.a, 15);
+
+ if (i == 142) {
+ // Next access to wrapper.b should throw.
+ nukeCCW(wrapper);
+ }
+ }
+ } catch (e) {
+ error = e;
+ }
+
+ assertEq(error.message.includes("dead object"), true);
+ assertEq(i, 143);
+}
+
+function testSweep() {
+ var wrapper = evaluate("({a: 15, b: {c: 42}})",
+ {global: newGlobal({newCompartment: true})});
+ var error;
+ nukeCCW(wrapper);
+ gczeal(8, 1); // Sweep zones separately
+ try {
+ // Next access to wrapper.b should throw.
+ wrapper.x = 4;
+ } catch (e) {
+ error = e;
+ }
+ assertEq(error.message.includes("dead object"), true);
+}
+
+testNuke();
+testSweep();
diff --git a/js/src/jit-test/tests/cacheir/number-parseInt-double.js b/js/src/jit-test/tests/cacheir/number-parseInt-double.js
new file mode 100644
index 0000000000..c4c133480b
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/number-parseInt-double.js
@@ -0,0 +1,205 @@
+// Test inlining parseInt with a Double input.
+
+const doubleValues = [
+ // Values around INT32_MIN.
+ -2147483648.5,
+ -2147483647.5,
+ -2147483646.5,
+
+ // Negative values.
+ -65536.1, -65535.2, -256.3, -255.4, -100.5, -50.6, -10.7,
+
+ // Values around zero.
+ -2.1, -1.1, -0, +0, 0.1, 1.1, 2.1,
+
+ // Positive values.
+ 10.7, 50.6, 100.5, 255.4, 256.3, 65535.2, 65536.1,
+
+ // Values around INT32_MAX.
+ 2147483645.5,
+ 2147483646.5,
+ 2147483647.5,
+];
+
+// Test double input without an explicit radix.
+function testRadixAbsent() {
+ for (let i = 0; i < 200; ++i) {
+ let x = doubleValues[i % doubleValues.length];
+ let y = x|0;
+
+ let r = Number.parseInt(x);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixAbsent();
+
+// Test double input with radix=10.
+function testRadixTen() {
+ for (let i = 0; i < 200; ++i) {
+ let x = doubleValues[i % doubleValues.length];
+
+ let r = Number.parseInt(x, 10);
+ assertEq(r, x|0);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixTen();
+
+// Test double input in the exclusive range (0, 1.0e-6).
+function testBadTooSmallPositive() {
+ const goodValues = [
+ +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5,
+ -0, -1.5, -2.5, -3.5, -4.5, -5.5,
+ ];
+ const badValues = [
+ 9.999999999999997e-7, // parseInt(9.999999999999997e-7) is 9.
+ 1e-7, // parseInt(1e-7) is 1.
+ ];
+
+ const values = [
+ ...goodValues,
+ ...badValues,
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let xs = [goodValues, values][(i >= 150)|0];
+ let x = xs[i % xs.length];
+ let y;
+ if (0 < x && x < 1e-6) {
+ y = (String(x).match(/(.*)e.*/)[1])|0;
+ } else {
+ y = x|0;
+ }
+
+ let r = Number.parseInt(x);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testBadTooSmallPositive();
+
+// Test double input in the exclusive range (-1.0e-6, -0).
+function testBadTooSmallNegative() {
+ const goodValues = [
+ +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5,
+ -0, -1.5, -2.5, -3.5, -4.5, -5.5,
+ ];
+ const badValues = [
+ -9.999999999999997e-7, // parseInt(-9.999999999999997e-7) is -9.
+ -1e-7, // parseInt(-1e-7) is -1.
+ ];
+
+ const values = [
+ ...goodValues,
+ ...badValues,
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let xs = [goodValues, values][(i >= 150)|0];
+ let x = xs[i % xs.length];
+ let y;
+ if (-1e-6 < x && x < -0) {
+ y = (String(x).match(/(.*)e.*/)[1])|0;
+ } else {
+ y = x|0;
+ }
+
+ let r = Number.parseInt(x);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testBadTooSmallNegative();
+
+// Test double input in the exclusive range (-1, -1.0e-6).
+function testBadNegativeZero() {
+ const goodValues = [
+ +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5,
+ -0, -1.5, -2.5, -3.5, -4.5, -5.5,
+ ];
+ const badValues = [
+ -0.1, // parseInt(-0.1) is -0.
+ -0.5, // parseInt(-0.5) is -0.
+ -0.9, // parseInt(-0.9) is -0.
+ ];
+
+ const values = [
+ ...goodValues,
+ ...badValues,
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let xs = [goodValues, values][(i >= 150)|0];
+ let x = xs[i % xs.length];
+ let y;
+ if (-1 < x && x < 0) {
+ y = -0;
+ } else {
+ y = x|0;
+ }
+
+ let r = Number.parseInt(x);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testBadNegativeZero();
+
+// Test double input with infinity values.
+function testBadInfinity() {
+ const goodValues = [
+ +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5,
+ -0, -1.5, -2.5, -3.5, -4.5, -5.5,
+ ];
+ const badValues = [
+ Infinity, // parseInt(Infinity) is NaN
+ -Infinity, // parseInt(-Infinity) is NaN
+ ];
+
+ const values = [
+ ...goodValues,
+ ...badValues,
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let xs = [goodValues, values][(i >= 150)|0];
+ let x = xs[i % xs.length];
+ let y;
+ if (!Number.isFinite(x)) {
+ y = NaN;
+ } else {
+ y = x|0;
+ }
+
+ let r = Number.parseInt(x);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testBadInfinity();
+
+// Test double input with NaN values.
+function testBadNaN() {
+ const goodValues = [
+ +0, +0.5, +1.5, +2.5, +3.5, +4.5, +5.5,
+ -0, -1.5, -2.5, -3.5, -4.5, -5.5,
+ ];
+ const badValues = [
+ NaN, // parseInt(NaN) is NaN
+ ];
+
+ const values = [
+ ...goodValues,
+ ...badValues,
+ ];
+
+ for (let i = 0; i < 200; ++i) {
+ let xs = [goodValues, values][(i >= 150)|0];
+ let x = xs[i % xs.length];
+ let y;
+ if (!Number.isFinite(x)) {
+ y = NaN;
+ } else {
+ y = x|0;
+ }
+
+ let r = Number.parseInt(x);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testBadNaN();
diff --git a/js/src/jit-test/tests/cacheir/number-parseInt-int32.js b/js/src/jit-test/tests/cacheir/number-parseInt-int32.js
new file mode 100644
index 0000000000..42def800de
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/number-parseInt-int32.js
@@ -0,0 +1,93 @@
+// Test inlining parseInt with an Int32 input.
+
+const int32Values = [
+ // Values around INT32_MIN.
+ -2147483648,
+ -2147483647,
+ -2147483646,
+
+ // Negative values.
+ -65536, -65535, -256, -255, -100, -50, -10,
+
+ // Values around zero.
+ -2, -1, 0, 1, 2,
+
+ // Positive values.
+ 10, 50, 100, 255, 256, 65535, 65536,
+
+ // Values around INT32_MAX.
+ 2147483645,
+ 2147483646,
+ 2147483647,
+];
+
+// Test int32 input without an explicit radix.
+function testRadixAbsent() {
+ for (let i = 0; i < 200; ++i) {
+ let x = int32Values[i % int32Values.length];
+ assertEq(x, x|0, "x is an int32 value");
+
+ let r = Number.parseInt(x);
+ assertEq(r, x);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixAbsent();
+
+// Test int32 input with radix=10.
+function testRadixTen() {
+ for (let i = 0; i < 200; ++i) {
+ let x = int32Values[i % int32Values.length];
+ assertEq(x, x|0, "x is an int32 value");
+
+ let r = Number.parseInt(x, 10);
+ assertEq(r, x);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixTen();
+
+// Test int32 input with radix=16. (This case isn't currently inlined.)
+function testRadixSixteen() {
+ for (let i = 0; i < 200; ++i) {
+ let x = int32Values[i % int32Values.length];
+ assertEq(x, x|0, "x is an int32 value");
+
+ let expected = Math.sign(x) * Number("0x" + Math.abs(x).toString(10));
+
+ let r = Number.parseInt(x, 16);
+ assertEq(r, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixSixteen();
+
+// Test with variable radix.
+function testRadixVariable() {
+ for (let i = 0; i < 200; ++i) {
+ let x = int32Values[i % int32Values.length];
+ assertEq(x, x|0, "x is an int32 value");
+
+ let radix = [10, 16][(i > 100)|0];
+
+ let expected = x;
+ if (radix === 16) {
+ expected = Math.sign(x) * Number("0x" + Math.abs(x).toString(10));
+ }
+
+ let r = Number.parseInt(x, radix);
+ assertEq(r, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixVariable();
+
+// Test with int32 and double inputs.
+function testBadInput() {
+ for (let i = 0; i < 200; ++i) {
+ let x = int32Values[i % int32Values.length];
+ assertEq(x, x|0, "x is an int32 value");
+
+ let y = [x, NaN][(i > 150)|0];
+
+ let r = Number.parseInt(y, 10);
+ assertEq(r, y);
+ }
+}
+for (let i = 0; i < 2; ++i) testBadInput();
diff --git a/js/src/jit-test/tests/cacheir/number-parseInt-string.js b/js/src/jit-test/tests/cacheir/number-parseInt-string.js
new file mode 100644
index 0000000000..7206c14db2
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/number-parseInt-string.js
@@ -0,0 +1,144 @@
+// Test inlining parseInt with a String input.
+
+const stringInt32Values = [
+ // Values around INT32_MIN.
+ "-2147483648",
+ "-2147483647",
+ "-2147483646",
+
+ // Negative values.
+ "-65536", "-65535", "-256", "-255", "-100", "-50", "-10",
+
+ // Values around zero.
+ "-2", "-1", "0", "1", "2",
+
+ // Positive values.
+ "10", "50", "100", "255", "256", "65535", "65536",
+
+ // Values around INT32_MAX.
+ "2147483645",
+ "2147483646",
+ "2147483647",
+];
+
+const stringInt32HexValues = [
+ // Values around INT32_MIN.
+ "-0x80000000",
+ "-0x7fffffff",
+ "-0x7ffffffe",
+
+ // Negative values.
+ "-0x10000", "-0xffff", "-0x100", "-0xff", "-0x64", "-0x32", "-0xa",
+
+ // Values around zero.
+ "-0x2", "-0x1", "0x0", "0x1", "0x2",
+
+ // Positive values.
+ "0xa", "0x32", "0x64", "0xff", "0x100", "0xffff", "0x10000",
+
+ // Values around INT32_MAX.
+ "0x7ffffffd",
+ "0x7ffffffe",
+ "0x7fffffff",
+];
+
+// Test string-int32 input without an explicit radix.
+function testRadixAbsent() {
+ for (let i = 0; i < 200; ++i) {
+ let x = stringInt32Values[i % stringInt32Values.length];
+ assertEq(+x, x|0, "x is an int32 value");
+
+ let r = Number.parseInt(x);
+ assertEq(r, +x);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixAbsent();
+
+// Test string-int32 hex input without an explicit radix.
+function testRadixAbsentHex() {
+ for (let i = 0; i < 200; ++i) {
+ let x = stringInt32HexValues[i % stringInt32HexValues.length];
+
+ // String to number conversion doesn't support negative hex-strings, so we
+ // have to chop off the leading minus sign manually.
+ let y = x;
+ let sign = 1;
+ if (x.startsWith("-")) {
+ y = x.slice(1);
+ sign = -1;
+ }
+
+ assertEq((+y) * sign, ((+y) * sign)|0, "x is an int32 hex value");
+
+ let r = Number.parseInt(x);
+ assertEq(r, (+y) * sign);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixAbsentHex();
+
+// Test string-int32 input with radix=10.
+function testRadixTen() {
+ for (let i = 0; i < 200; ++i) {
+ let x = stringInt32Values[i % stringInt32Values.length];
+ assertEq(+x, x|0, "x is an int32 value");
+
+ let r = Number.parseInt(x, 10);
+ assertEq(r, +x);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixTen();
+
+// Test string-int32 input with radix=16. (This case isn't currently inlined.)
+function testRadixSixteen() {
+ for (let i = 0; i < 200; ++i) {
+ let x = stringInt32Values[i % stringInt32Values.length];
+ assertEq(+x, x|0, "x is an int32 value");
+
+ let expected = Math.sign(x) * Number("0x" + Math.abs(x).toString(10));
+
+ let r = Number.parseInt(x, 16);
+ assertEq(r, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixSixteen();
+
+// Test string-int32 hex input with radix=16. (This case isn't currently inlined.)
+function testRadixSixteenHex() {
+ for (let i = 0; i < 200; ++i) {
+ let x = stringInt32HexValues[i % stringInt32HexValues.length];
+
+ // String to number conversion doesn't support negative hex-strings, so we
+ // have to chop off the leading minus sign manually.
+ let y = x;
+ let sign = 1;
+ if (x.startsWith("-")) {
+ y = x.slice(1);
+ sign = -1;
+ }
+
+ assertEq((+y) * sign, ((+y) * sign)|0, "x is an int32 hex value");
+
+ let r = Number.parseInt(x, 16);
+ assertEq(r, (+y) * sign);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixSixteenHex();
+
+// Test with variable radix.
+function testRadixVariable() {
+ for (let i = 0; i < 200; ++i) {
+ let x = stringInt32Values[i % stringInt32Values.length];
+ assertEq(+x, x|0, "x is an int32 value");
+
+ let radix = [10, 16][(i > 100)|0];
+
+ let expected = +x;
+ if (radix === 16) {
+ expected = Math.sign(+x) * Number("0x" + Math.abs(+x).toString(10));
+ }
+
+ let r = Number.parseInt(x, radix);
+ assertEq(r, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) testRadixVariable();
diff --git a/js/src/jit-test/tests/cacheir/number-toString.js b/js/src/jit-test/tests/cacheir/number-toString.js
new file mode 100644
index 0000000000..ed076a6753
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/number-toString.js
@@ -0,0 +1,44 @@
+function int32() {
+ var n = 42;
+ for (var i = 0; i < 100; i++) {
+ assertEq(n.toString(), "42");
+ }
+}
+
+function double() {
+ var n = 3.14;
+ for (var i = 0; i < 100; i++) {
+ assertEq(n.toString(), "3.14");
+ }
+}
+
+function number() {
+ var n = 1;
+ for (var i = 0; i < 100; i++) {
+ assertEq(n.toString(), i > 50 ? "3.14" : "1");
+ if (i == 50) {
+ n = 3.14;
+ }
+ }
+}
+
+function obj() {
+ var o = new Number(42);
+ for (var i = 0; i < 100; i++) {
+ assertEq(o.toString(), "42");
+ }
+}
+
+function overwritten() {
+ Number.prototype.toString = () => "haha";
+ var n = 42;
+ for (var i = 0; i < 100; i++) {
+ assertEq(n.toString(), "haha");
+ }
+}
+
+int32();
+double();
+number();
+obj();
+overwritten();
diff --git a/js/src/jit-test/tests/cacheir/object-addprop-hook.js b/js/src/jit-test/tests/cacheir/object-addprop-hook.js
new file mode 100644
index 0000000000..f138ed0b65
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/object-addprop-hook.js
@@ -0,0 +1,18 @@
+function test() {
+ var sym = Symbol();
+ for (var i = 0; i < 100; i++) {
+ var obj = newObjectWithAddPropertyHook();
+ assertEq(obj._propertiesAdded, 0);
+ obj.x = 1;
+ obj.y = 2;
+ obj.z = 3;
+ obj[sym] = 4;
+ obj[0] = 1;
+ obj[1234567] = 1;
+ assertEq(obj._propertiesAdded, 6);
+ assertEq(obj.x, 1);
+ assertEq(obj[sym], 4);
+ assertEq(obj[0], 1);
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js b/js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js
new file mode 100644
index 0000000000..7568f17885
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/object-constructor-metadata-builder.js
@@ -0,0 +1,14 @@
+let capture = [];
+
+for (let i = 0; i <= 200; ++i) {
+ if (i === 100) {
+ enableTrackAllocations();
+ }
+
+ // Create a new object through `new Object` and capture the result.
+ capture[i & 1] = new Object();
+
+ // Ensure the allocation is properly tracked when inlining `new Object` in CacheIR.
+ let data = getAllocationMetadata(capture[i & 1]);
+ assertEq(data !== null, i >= 100);
+}
diff --git a/js/src/jit-test/tests/cacheir/object-constructor.js b/js/src/jit-test/tests/cacheir/object-constructor.js
new file mode 100644
index 0000000000..144e732a09
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/object-constructor.js
@@ -0,0 +1,76 @@
+// Test various inlinable Object constructor calls.
+
+function callNoArgs() {
+ for (let i = 0; i < 100; ++i) {
+ let obj = Object();
+
+ // Creates a new empty object.
+ assertEq(Reflect.getPrototypeOf(obj), Object.prototype);
+ assertEq(Reflect.ownKeys(obj).length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) callNoArgs();
+
+function constructNoArgs() {
+ for (let i = 0; i < 100; ++i) {
+ let obj = new Object();
+
+ // Creates a new empty object.
+ assertEq(Reflect.getPrototypeOf(obj), Object.prototype);
+ assertEq(Reflect.ownKeys(obj).length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) constructNoArgs();
+
+function funCallNoArgs() {
+ // NB: Function.prototype.call is only inlined when the thisValue argument is present.
+ const thisValue = null;
+
+ for (let i = 0; i < 100; ++i) {
+ let obj = Object.call(thisValue);
+
+ // Creates a new empty object.
+ assertEq(Reflect.getPrototypeOf(obj), Object.prototype);
+ assertEq(Reflect.ownKeys(obj).length, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) funCallNoArgs();
+
+function callObjectArg() {
+ let xs = [{}, {}];
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ let obj = Object(x);
+
+ // Returns the input object.
+ assertEq(obj, x);
+ }
+}
+for (let i = 0; i < 2; ++i) callObjectArg();
+
+function constructObjectArg() {
+ let xs = [{}, {}];
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ let obj = new Object(x);
+
+ // Returns the input object.
+ assertEq(obj, x);
+ }
+}
+for (let i = 0; i < 2; ++i) constructObjectArg();
+
+function funCallObjectArg() {
+ // NB: Function.prototype.call is only inlined when the thisValue argument is present.
+ const thisValue = null;
+
+ let xs = [{}, {}];
+ for (let i = 0; i < 100; ++i) {
+ let x = xs[i & 1];
+ let obj = Object.call(thisValue, x);
+
+ // Returns the input object.
+ assertEq(obj, x);
+ }
+}
+for (let i = 0; i < 2; ++i) funCallObjectArg();
diff --git a/js/src/jit-test/tests/cacheir/object-is-prototype-of.js b/js/src/jit-test/tests/cacheir/object-is-prototype-of.js
new file mode 100644
index 0000000000..1ecc441f47
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/object-is-prototype-of.js
@@ -0,0 +1,41 @@
+function testPrimitive() {
+ for (var i = 0; i < 100; ++i) {
+ // Null and undefined.
+ assertEq(Object.prototype.isPrototypeOf(null), false);
+ assertEq(Object.prototype.isPrototypeOf(void 0), false);
+
+ // Primitive wrappers.
+ assertEq(String.prototype.isPrototypeOf(""), false);
+ assertEq(Number.prototype.isPrototypeOf(0), false);
+ assertEq(Boolean.prototype.isPrototypeOf(true), false);
+ assertEq(BigInt.prototype.isPrototypeOf(0n), false);
+ assertEq(Symbol.prototype.isPrototypeOf(Symbol.hasInstance), false);
+ }
+}
+testPrimitive();
+
+function testObject() {
+ for (var i = 0; i < 100; ++i) {
+ assertEq(Object.prototype.isPrototypeOf({}), true);
+ assertEq(Object.prototype.isPrototypeOf([]), true);
+
+ assertEq(Array.prototype.isPrototypeOf({}), false);
+ assertEq(Array.prototype.isPrototypeOf([]), true);
+ }
+}
+testObject();
+
+function testProxy() {
+ var proxy = new Proxy({}, new Proxy({}, {
+ get(t, pk, r) {
+ assertEq(pk, "getPrototypeOf");
+ return Reflect.get(t, pk, r);
+ }
+ }));
+
+ for (var i = 0; i < 100; ++i) {
+ assertEq(Object.prototype.isPrototypeOf(proxy), true);
+ assertEq(Array.prototype.isPrototypeOf(proxy), false);
+ }
+}
+testProxy();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js
new file mode 100644
index 0000000000..f22d5fab63
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-1.js
@@ -0,0 +1,13 @@
+(() => {
+ let returnCalled = false;
+ ({}).__proto__.return = () => {
+ returnCalled = true;
+ return { value: 3, done: true };
+ };
+
+ assertEq(returnCalled, false);
+ let [a,b] = [1,2,3];
+ assertEq(returnCalled, true);
+ assertEq(a, 1);
+ assertEq(b, 2);
+})();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js
new file mode 100644
index 0000000000..83c568dac0
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-2.js
@@ -0,0 +1,17 @@
+(() => {
+ let returnCalled = false;
+
+ function foo() {
+ ({}).__proto__.return = () => {
+ returnCalled = true;
+ return { value: 3, done: true };
+ };
+ return 2;
+ }
+
+ assertEq(returnCalled, false);
+ let [a,[b=foo()]] = [1,[],3];
+ assertEq(returnCalled, true);
+ assertEq(a, 1);
+ assertEq(b, 2);
+})();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js
new file mode 100644
index 0000000000..3fe8593b54
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-3.js
@@ -0,0 +1,13 @@
+(() => {
+ let nextCalled = 0;
+ ([])[Symbol.iterator]().__proto__.next = () => {
+ nextCalled++;
+ return {value: nextCalled, done: false};
+ };
+
+ assertEq(nextCalled, 0);
+ let [a,b] = [1,2,3];
+ assertEq(nextCalled, 2);
+ assertEq(a, 1);
+ assertEq(b, 2);
+})();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js
new file mode 100644
index 0000000000..a1ee65e784
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-4.js
@@ -0,0 +1,36 @@
+(() => {
+ let iterablesBase = [
+ [1,2],
+ [1,2,3],
+ [1,2,3],
+ [3,2,1],
+ ];
+
+ let iterables = [];
+ for (let i = 0; i < 1000; i++) {
+ iterables.push([...iterablesBase[i % iterablesBase.length]]);
+ }
+
+ iterables.push(new Map([[1, 3], [2,4]]).keys());
+
+ function testDestructuringInitialization(a) {
+ let [x,y] = a;
+ return y;
+ }
+
+ function testDestructuringAssignment(a) {
+ let x, y;
+ [x,y] = a;
+ return y;
+ }
+
+ for (let i = 0; i < iterables.length; i++) {
+ assertEq(testDestructuringInitialization(iterables[i]), 2);
+ }
+
+ // refresh the last iterator
+ iterables[iterables.length - 1] = new Map([[1, 3], [2,4]]).keys();
+ for (let i = 0; i < iterables.length; i++) {
+ assertEq(testDestructuringAssignment(iterables[i]), 2);
+ }
+})();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js
new file mode 100644
index 0000000000..42aab01057
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-5.js
@@ -0,0 +1,19 @@
+(() => {
+ var returnCalled = false;
+ Object.defineProperty(globalThis, 'x', {
+ get() {
+ return 42;
+ },
+ set(value) {
+ ({}).__proto__.return = () => {
+ returnCalled = true;
+ return { value: 3, done: true };
+ };
+ }
+ });
+
+ [x] = [1, 2];
+
+ assertEq(x, 42);
+ assertEq(returnCalled, true);
+})();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js
new file mode 100644
index 0000000000..059914afc1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-6.js
@@ -0,0 +1,7 @@
+(() => {
+ ({}).__proto__[1] = 2;
+ let [x,y] = [1];
+
+ assertEq(x, 1);
+ assertEq(y, undefined);
+})();
diff --git a/js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js b/js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js
new file mode 100644
index 0000000000..e6e7a6269e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-get-iterator-7.js
@@ -0,0 +1,10 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+function f(arr) {
+ var [a, b] = arr;
+ return b;
+}
+
+for (var i = 0; i < 10_000; ++i) {
+ assertEq(f([0, 1]), 1);
+}
diff --git a/js/src/jit-test/tests/cacheir/optimize-spread.js b/js/src/jit-test/tests/cacheir/optimize-spread.js
new file mode 100644
index 0000000000..accd56ea13
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/optimize-spread.js
@@ -0,0 +1,16 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+function testOptimizeSpread() {
+ function f(a, b) {
+ return a + b;
+ }
+ function g(...rest) {
+ return f(...rest);
+ }
+
+ for (var i = 0; i < 20; ++i) {
+ var v = g(1, 2);
+ assertEq(v, 3);
+ }
+}
+for (var i = 0; i < 2; ++i) testOptimizeSpread();
diff --git a/js/src/jit-test/tests/cacheir/parseInt-double-truncate.js b/js/src/jit-test/tests/cacheir/parseInt-double-truncate.js
new file mode 100644
index 0000000000..2b989f2624
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/parseInt-double-truncate.js
@@ -0,0 +1,41 @@
+// Test some double-truncation edge cases with parseInt(double).
+
+function testPos1() {
+ with({}) {}
+ var fun = d => parseInt(d);
+ for (var i = 0; i < 2000; i++) {
+ assertEq(fun(i + 0.5), i);
+ }
+ assertEq(fun(0xf_ffff_ffff) + 345, 68719477080);
+}
+testPos1();
+
+function testPos2() {
+ with({}) {}
+ var fun = d => parseInt(d);
+ for (var i = 0; i < 2000; i++) {
+ assertEq(fun(i + 0.5), i);
+ }
+ assertEq(fun(0x8000_0000) + 345, 2147483993);
+}
+testPos2();
+
+function testNeg1() {
+ with({}) {}
+ var fun = d => parseInt(d);
+ for (var i = 0; i < 2000; i++) {
+ assertEq(fun(i + 0.5), i);
+ }
+ assertEq(fun(-0xf_ffff_ffff) - 345, -68719477080);
+}
+testNeg1();
+
+function testNeg2() {
+ with({}) {}
+ var fun = d => parseInt(d);
+ for (var i = 0; i < 2000; i++) {
+ assertEq(fun(i + 0.5), i);
+ }
+ assertEq(fun(-0x8000_0001) - 345, -2147483994);
+}
+testNeg2();
diff --git a/js/src/jit-test/tests/cacheir/rope-char-at.js b/js/src/jit-test/tests/cacheir/rope-char-at.js
new file mode 100644
index 0000000000..6b864035d7
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/rope-char-at.js
@@ -0,0 +1,20 @@
+function test(a, b, firstCharCode) {
+ var s = newRope(a, b);
+ for (var i = 0; i < s.length; i++) {
+ assertEq(s.charCodeAt(i), firstCharCode + i);
+ assertEq(s.charAt(i), String.fromCharCode(firstCharCode + i));
+ }
+ // charAt/charCodeAt support one-level deep ropes without linearizing.
+ assertEq(isRope(s), true);
+ assertEq(isRope(a), false);
+ assertEq(isRope(b), false);
+}
+test("abcdefghijkl", "mnopqrstuvwxyz", 97);
+test("a", "bcdefghijklmnopqrstuvwxyz", 97);
+test("abcdefghijklmnopqrstuvwxy", "z", 97);
+test("0123456789:;<=>?@ABCDEFGHIJ", "K", 48);
+test("\u00fe\u00ff", "\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010A", 0xfe);
+test("\u1000\u1001\u1002", "\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100A\u100B\u100C", 4096);
+
+// charAt/charCodeAt stubs currently fail for nested ropes.
+test("ab", newRope("cdefghijklmnop", "qrstuvwxyz{|}~"), 97);
diff --git a/js/src/jit-test/tests/cacheir/set-has-bigint.js b/js/src/jit-test/tests/cacheir/set-has-bigint.js
new file mode 100644
index 0000000000..24c8889e99
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-bigint.js
@@ -0,0 +1,84 @@
+// Return a new set, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createSet(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Set(xs);
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testInlineDigitsSameSign(n) {
+ var xs = [1n, 2n];
+ var ys = [3n, 4n];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testInlineDigitsSameSign);
+
+function testInlineDigitsDifferentSign(n) {
+ var xs = [-1n, 2n];
+ var ys = [1n, -2n];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testInlineDigitsDifferentSign);
+
+function testHeapDigitsSameSign(n) {
+ // Definitely uses heap digits.
+ var heap = 2n ** 1000n;
+
+ var xs = [heap + 1n, heap + 2n];
+ var ys = [heap + 3n, heap + 4n];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testHeapDigitsSameSign);
+
+function testHeapDigitsDifferentSign(n) {
+ // Definitely uses heap digits.
+ var heap = 2n ** 1000n;
+
+ var xs = [-(heap + 1n), heap + 2n];
+ var ys = [heap + 1n, -(heap + 2n)];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testHeapDigitsDifferentSign);
diff --git a/js/src/jit-test/tests/cacheir/set-has-nongcthing.js b/js/src/jit-test/tests/cacheir/set-has-nongcthing.js
new file mode 100644
index 0000000000..a5a64e0333
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-nongcthing.js
@@ -0,0 +1,110 @@
+// Return a new set, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createSet(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Set(xs);
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testInt32(n) {
+ var xs = [1, 2];
+ var ys = [3, 4];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testInt32);
+
+function testDouble(n) {
+ var xs = [Math.PI, Infinity];
+ var ys = [Math.E, -Infinity];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testDouble);
+
+function testZero(n) {
+ var xs = [0, -0];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set = createSet([0], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testZero);
+
+function testNaN(n) {
+ var xs = [NaN, -NaN];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set = createSet([NaN], n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testNaN);
+
+function testUndefinedAndNull(n) {
+ var xs = [undefined, null];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testUndefinedAndNull);
+
+function testBoolean(n) {
+ var xs = [true, false];
+ var ys = [1, -1];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testBoolean);
diff --git a/js/src/jit-test/tests/cacheir/set-has-object.js b/js/src/jit-test/tests/cacheir/set-has-object.js
new file mode 100644
index 0000000000..ea1b690d70
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-object.js
@@ -0,0 +1,30 @@
+// Return a new set, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createSet(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Set(xs);
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function test(n) {
+ var xs = [{}, {}];
+ var ys = [{}, {}];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(test);
diff --git a/js/src/jit-test/tests/cacheir/set-has-string-gczeal.js b/js/src/jit-test/tests/cacheir/set-has-string-gczeal.js
new file mode 100644
index 0000000000..9ef81634d6
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-string-gczeal.js
@@ -0,0 +1,17 @@
+// Ensure string hashing works correctly under gczeal=compact.
+
+gczeal(14)
+
+function test() {
+ var set = new Set();
+ var c = 0;
+ var N = 1000;
+ for (var i = 0; i < N; ++i) {
+ var k = String.fromCodePoint(i);
+ set.add(k);
+ if (set.has(k)) c++;
+ }
+ assertEq(c, N);
+}
+
+test();
diff --git a/js/src/jit-test/tests/cacheir/set-has-string.js b/js/src/jit-test/tests/cacheir/set-has-string.js
new file mode 100644
index 0000000000..eac19378c4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-string.js
@@ -0,0 +1,79 @@
+// Return a new set, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createSet(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Set(xs);
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testConstant(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testConstant);
+
+function testConstantFatInline(n) {
+ var xs = ["a", "b"].map(s => s.repeat(10));
+ var ys = ["c", "d"].map(s => s.repeat(10));
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testConstantFatInline);
+
+function testComputed(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ z = String.fromCharCode(z.charCodeAt(0));
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testComputed);
+
+function testRope(n) {
+ var xs = ["a", "b"];
+ var ys = ["c", "d"];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs.map(x => x.repeat(100)), n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3].repeat(100);
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testRope);
diff --git a/js/src/jit-test/tests/cacheir/set-has-symbol.js b/js/src/jit-test/tests/cacheir/set-has-symbol.js
new file mode 100644
index 0000000000..35e4f11267
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-symbol.js
@@ -0,0 +1,30 @@
+// Return a new set, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createSet(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Set(xs);
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function test(n) {
+ var xs = [Symbol(), Symbol()];
+ var ys = [Symbol(), Symbol()];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 100;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 3];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(test);
diff --git a/js/src/jit-test/tests/cacheir/set-has-value.js b/js/src/jit-test/tests/cacheir/set-has-value.js
new file mode 100644
index 0000000000..af03bf02e3
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-has-value.js
@@ -0,0 +1,30 @@
+// Return a new set, possibly filling some dummy entries to enforce creating
+// multiple hash buckets.
+function createSet(values, n) {
+ var xs = [...values];
+ for (var i = 0; i < n; ++i) {
+ xs.push({});
+ }
+ return new Set(xs);
+}
+
+function runTest(fn) {
+ fn(0);
+ fn(100);
+}
+
+function testPolymorphic(n) {
+ var xs = [10, 10.5, "test", Symbol("?"), 123n, -123n, {}, []];
+ var ys = [-0, NaN, "bad", Symbol("!"), 42n, -99n, {}, []];
+ var zs = [...xs, ...ys];
+ var set = createSet(xs, n);
+
+ var N = 128;
+ var c = 0;
+ for (var i = 0; i < N; ++i) {
+ var z = zs[i & 15];
+ if (set.has(z)) c++;
+ }
+ assertEq(c, N / 2);
+}
+runTest(testPolymorphic);
diff --git a/js/src/jit-test/tests/cacheir/set-size.js b/js/src/jit-test/tests/cacheir/set-size.js
new file mode 100644
index 0000000000..894ed83be7
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/set-size.js
@@ -0,0 +1,43 @@
+function testEmpty() {
+ let set = new Set();
+ for (let i = 0; i < 100; ++i) {
+ assertEq(set.size, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) testEmpty();
+
+function testSimple() {
+ let set = new Set([1, 2, 3, 4]);
+ for (let i = 0; i < 100; ++i) {
+ assertEq(set.size, 4);
+ }
+}
+for (let i = 0; i < 2; ++i) testSimple();
+
+function testWithDelete() {
+ for (let i = 0; i < 100; ++i) {
+ let a = [1, 2, 3, 4];
+ let set = new Set(a);
+ for (let j = 0; j < a.length; ++j) {
+ assertEq(set.size, a.length - j);
+ set.delete(a[j]);
+ assertEq(set.size, a.length - j - 1);
+ }
+ assertEq(set.size, 0);
+ }
+}
+for (let i = 0; i < 2; ++i) testWithDelete();
+
+function testWithAdd() {
+ for (let i = 0; i < 100; ++i) {
+ let a = [1, 2, 3, 4];
+ let set = new Set();
+ for (let j = 0; j < a.length; ++j) {
+ assertEq(set.size, j);
+ set.add(a[j]);
+ assertEq(set.size, j + 1);
+ }
+ assertEq(set.size, a.length);
+ }
+}
+for (let i = 0; i < 2; ++i) testWithAdd();
diff --git a/js/src/jit-test/tests/cacheir/setelem-id-guard.js b/js/src/jit-test/tests/cacheir/setelem-id-guard.js
new file mode 100644
index 0000000000..5f5ef82c58
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setelem-id-guard.js
@@ -0,0 +1,91 @@
+function setSlot() {
+ var o = {x: 1, y: 2};
+ for (var i=0; i<100; i++) {
+ var p = (i & 1) ? "x" : "y";
+ o[p] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ }
+}
+setSlot();
+
+function setUnboxed() {
+ var arr = [];
+ for (var i=0; i<100; i++)
+ arr.push({x: 1, y: 2});
+
+ for (var i=0; i<100; i++) {
+ var o = arr[i];
+ var p = (i & 1) ? "x" : "y";
+ o[p] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ }
+}
+setUnboxed();
+
+function setUnboxedExpando() {
+ var arr = [];
+ for (var i=0; i<100; i++)
+ arr.push({x: 1, y: 2});
+
+ for (var i=0; i<100; i++) {
+ var o = arr[i];
+ o.a = 1;
+ o.b = 2;
+ var p = (i & 1) ? "a" : "b";
+ o[p] = i;
+ o[p] = o[p] + 4;
+ assertEq((i & 1) ? o.a : o.b, i + 4);
+ }
+}
+setUnboxedExpando();
+
+function setArrayLength() {
+ var arr = [];
+ for (var i=0; i<100; i++) {
+ var p = (i & 1) ? "length" : "x";
+ arr[p] = i;
+ assertEq((i & 1) ? arr.length : arr.x, i);
+ }
+}
+setArrayLength();
+
+function setter() {
+ var c = 0;
+ var o = {set x(i) { c += i; }, set y(i) { c += i + 2; }};
+ for (var i=0; i<100; i++) {
+ var p = (i & 1) ? "x" : "y";
+ o[p] = i;
+ }
+ assertEq(c, 5050);
+}
+setter();
+
+function addSlot() {
+ for (var i=0; i<100; i++) {
+ var o = {};
+ var p1 = (i & 1) ? "x" : "y";
+ var p2 = (i % 3) ? "a" : "b";
+ o[p1] = i;
+ o[p2] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ assertEq((i % 3) ? o.a : o.b, i);
+ }
+}
+addSlot();
+
+function addExpandoSlot() {
+ var arr = [];
+ for (var i=0; i<100; i++)
+ arr.push({c: 1, d: 2});
+
+ for (var i=0; i<100; i++) {
+ var o = arr[i];
+ var p1 = (i & 1) ? "x" : "y";
+ var p2 = (i % 3) ? "a" : "b";
+ o[p1] = i;
+ o[p2] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ assertEq((i % 3) ? o.a : o.b, i);
+ }
+}
+addExpandoSlot();
diff --git a/js/src/jit-test/tests/cacheir/setelem-undefined-null.js b/js/src/jit-test/tests/cacheir/setelem-undefined-null.js
new file mode 100644
index 0000000000..894fad5fe6
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setelem-undefined-null.js
@@ -0,0 +1,50 @@
+function exists() {
+ var a = {'null': 0, 'undefined': 0};
+ for (var i = 0; i < 100; i++) {
+ a[null] = i;
+ a[undefined] = i * 2;
+ assertEq(a['null'], i);
+ assertEq(a['undefined'], i * 2);
+ }
+}
+
+function adding() {
+ for (var i = 0; i < 100; i++) {
+ var a = {};
+ a[null] = i;
+ a[undefined] = i * 2;
+ assertEq(a['null'], i);
+ assertEq(a['undefined'], i * 2);
+ }
+}
+
+function setter() {
+ var test = 0;
+ var a = {
+ set null(v) {
+ test = v;
+ },
+ set undefined(v) {
+ test = v * 2;
+ }
+ }
+ for (var i = 0; i < 100; i++) {
+ a[null] = i;
+ assertEq(test, i);
+ a[undefined] = i;
+ assertEq(test, i * 2);
+ }
+}
+
+function mixed() {
+ var a = {'null': void 0, 'undefined': void 0};
+ for (var i = 0; i < 100; i++) {
+ a[i % 2 ? null : undefined] = i;
+ assertEq(a[i % 2 ? 'null' : 'undefined'], i)
+ }
+}
+
+exists();
+adding()
+setter();
+mixed();
diff --git a/js/src/jit-test/tests/cacheir/setgname-let.js b/js/src/jit-test/tests/cacheir/setgname-let.js
new file mode 100644
index 0000000000..92345fb0df
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setgname-let.js
@@ -0,0 +1,93 @@
+let x = 2;
+
+function simple() {
+ for (var i = 0; i < 10; i++) {
+ x = i;
+ assertEq(x, i);
+ }
+}
+
+function setname() {
+ function set(obj, v) {
+ with (obj) {
+ x = v;
+ }
+ }
+
+ set({}, 100)
+ assertEq(x, 100);
+ set({x: 1}, 0);
+ assertEq(x, 100);
+ set({__proto__: {x: 1}}, 13);
+ assertEq(x, 100);
+}
+
+
+function noshadow() {
+ for (var i = 0; i < 20; i++) {
+ x = i;
+ assertEq(x, i);
+
+ if (i == 10) {
+ globalThis.x = "haha";
+ assertEq(x, 10);
+ }
+ }
+}
+
+function uninitialized() {
+ for (var i = 0; i < 20; i++) {
+ var threw = false;
+ try {
+ undef = 2;
+ } catch {
+ threw = true;
+ }
+ assertEq(threw, true);
+ }
+}
+
+function simpleStrict() {
+ "use strict";
+ for (var i = 0; i < 10; i++) {
+ x = i;
+ assertEq(x, i);
+ }
+}
+
+// No with in strict!
+
+function noshadowStrict() {
+ "use strict";
+ for (var i = 0; i < 20; i++) {
+ x = i;
+ assertEq(x, i);
+
+ if (i == 10) {
+ globalThis.x = "haha";
+ assertEq(x, 10);
+ }
+ }
+}
+
+function uninitializedStrict() {
+ for (var i = 0; i < 20; i++) {
+ var threw = false;
+ try {
+ undef = 2;
+ } catch {
+ threw = true;
+ }
+ assertEq(threw, true);
+ }
+}
+
+simple();
+setname();
+noshadow();
+uninitialized();
+simpleStrict();
+noshadowStrict();
+uninitializedStrict();
+
+let undef = 42;
diff --git a/js/src/jit-test/tests/cacheir/setter-is-native.js b/js/src/jit-test/tests/cacheir/setter-is-native.js
new file mode 100644
index 0000000000..28949b0640
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setter-is-native.js
@@ -0,0 +1,15 @@
+// Make sure we use an IC call.
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+// Assume |eval| is always a native function.
+var obj = Object.defineProperty({}, "prop", {
+ set: eval
+});
+
+var p;
+for (let i = 0; i < 1000; ++i) {
+ // Call the native setter (eval).
+ obj.prop = `p = ${i}`;
+
+ assertEq(p, i);
+}
diff --git a/js/src/jit-test/tests/cacheir/shape-teleporting-1.js b/js/src/jit-test/tests/cacheir/shape-teleporting-1.js
new file mode 100644
index 0000000000..35deb8c34f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/shape-teleporting-1.js
@@ -0,0 +1,128 @@
+// Receiver shadows
+(function() {
+ function check(p) { return p.x; }
+
+ let a = { x: "a" };
+ let b = { __proto__: a };
+ let c = { __proto__: b };
+ let d = { __proto__: c };
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ d.x = "d";
+ assertEq(check(d), "d");
+})();
+
+// Intermediate proto shadows
+(function() {
+ function check(p) { return p.x; }
+
+ let a = { x: "a" };
+ let b = { __proto__: a };
+ let c = { __proto__: b };
+ let d = { __proto__: c };
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ c.x = "c";
+ assertEq(check(d), "c");
+})();
+
+// Receiver proto changes
+(function() {
+ function check(p) { return p.x; }
+
+ let a = { x: "a" };
+ let b = { __proto__: a };
+ let c = { __proto__: b };
+ let d = { __proto__: c };
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ d.__proto__ = { x: "?" };
+ assertEq(check(d), "?");
+})();
+
+// Intermediate proto changes
+(function() {
+ function check(p) { return p.x; }
+
+ let a = { x: "a" };
+ let b = { __proto__: a };
+ let c = { __proto__: b };
+ let d = { __proto__: c };
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ c.__proto__ = { x: "?" };
+ assertEq(check(d), "?");
+})();
+
+// Uncacheable holder proto
+(function() {
+ function check(p) { return p.x; }
+
+ function Base() { this.x = "a"; }
+ let a = new Base;
+ a.__proto__ = new Object;
+ let b = { __proto__: a };
+ let c = { __proto__: b };
+ let d = { __proto__: c };
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ b.__proto__ = { x: "?" };
+ assertEq(check(d), "?");
+})();
+
+// Uncacheable intermediate proto
+(function() {
+ function check(p) { return p.x; }
+
+ function Base() { this.x = "a"; }
+ function Node() { }
+
+ let a = new Base;
+ let b = new Node; b.__proto__ = a;
+ let c = { __proto__: b };
+ let d = { __proto__: c };
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ b.__proto__ = { x: "?" };
+ assertEq(check(d), "?");
+})();
+
+// Uncacheable receiver proto
+(function() {
+ function check(p) { return p.x; }
+
+ function Base() { this.x = "a"; }
+ function Node() { }
+
+ let a = new Base;
+ let b = { __proto__: a };
+ let c = { __proto__: b };
+ let d = new Node; d.__proto__ = c;
+
+ assertEq(check(d), "a");
+ assertEq(check(d), "a");
+ d.__proto__ = { x: "?" };
+ assertEq(check(d), "?");
+})();
+
+// Uncacheable receiver proto (only receiver / holder)
+(function() {
+ function check(p) { return p.x; }
+
+ function Base() { this.x = "a"; }
+ function Node() { }
+
+ let a = new Base;
+ let b = new Node; b.__proto__ = a;
+
+ assertEq(check(b), "a");
+ assertEq(check(b), "a");
+ b.__proto__ = { x: "?" };
+ assertEq(check(b), "?");
+})();
diff --git a/js/src/jit-test/tests/cacheir/shape-teleporting-2.js b/js/src/jit-test/tests/cacheir/shape-teleporting-2.js
new file mode 100644
index 0000000000..1b103d7ec3
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/shape-teleporting-2.js
@@ -0,0 +1,47 @@
+function A(name) { this.name = name; }
+function B() { }
+function C() { }
+
+B.prototype = A0 = new A("0");
+C.prototype = B0 = new B();
+
+var A1 = new A("1");
+var A2 = new A("2");
+
+var B1 = new B();
+var B2 = new B();
+
+var C1 = new C();
+var C2 = new C();
+
+// Object <-+- A0 <-+- B0 <-+
+// | | |
+// +- A1 +- B1 +- C1
+// | | |
+// +- A2 +- B2 +- C2
+
+Object.setPrototypeOf(C1, B1);
+Object.setPrototypeOf(C2, B2);
+
+Object.setPrototypeOf(B1, A1);
+Object.setPrototypeOf(B2, A2);
+
+// Object <-+- A0 <--- B0
+// |
+// +- A1 <--- B1 <--- C1
+// |
+// +- A2 <--- B2 <--- C2
+
+
+function getName(o) { return o.name; }
+
+// Warm up JIT
+for (var i = 0; i < 100; i++) {
+ getName(C1);
+}
+
+assertEq(B1.name, "1");
+assertEq(B2.name, "2");
+
+assertEq(getName(B1), "1");
+assertEq(getName(B2), "2");
diff --git a/js/src/jit-test/tests/cacheir/shape-teleporting-3.js b/js/src/jit-test/tests/cacheir/shape-teleporting-3.js
new file mode 100644
index 0000000000..18349056c3
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/shape-teleporting-3.js
@@ -0,0 +1,32 @@
+function test1() {
+ var o1 = {x: 1, y: 2, z: 3};
+ var o2 = Object.create(o1);
+ var o3 = Object.create(o2);
+ o2.x = 2; // Ensure teleporting is invalidated for o1.
+ for (var i = 0; i < 30; i++) {
+ assertEq(o3.y, i > 20 ? -1 : 2);
+ if (i === 20) {
+ // Add a (second) shadowing property to o2. The property access
+ // above must detect this properly.
+ o2.y = -1;
+ }
+ }
+}
+test1();
+
+function test2() {
+ var o1 = {x: 1, y: 2, z: 3};
+ var o2 = Object.create(o1);
+ var o3 = Object.create(o2);
+ var o4 = Object.create(o3);
+ o2.x = 1;
+ o2.a = 2;
+ o3.a = 2;
+ for (var i = 0; i < 30; i++) {
+ assertEq(o4.y, i > 20 ? undefined : 2);
+ if (i === 20) {
+ o2.__proto__ = null;
+ }
+ }
+}
+test2();
diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-1.js b/js/src/jit-test/tests/cacheir/spread-minmax-1.js
new file mode 100644
index 0000000000..b1bd6e5692
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/spread-minmax-1.js
@@ -0,0 +1,23 @@
+function testMin(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.min(...arr);
+ }
+ return sum;
+}
+
+function testMax(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.max(...arr);
+ }
+ return sum;
+}
+
+// Attach Int32MinMaxArrayResult.
+assertEq(testMin([1,2,3,4,5]), 50);
+assertEq(testMax([1,2,3,4,5]), 250);
+
+// Verify that we handle an empty list correctly.
+assertEq(testMin([]), Infinity);
+assertEq(testMax([]), -Infinity);
diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-2.js b/js/src/jit-test/tests/cacheir/spread-minmax-2.js
new file mode 100644
index 0000000000..4cc3b0d9d6
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/spread-minmax-2.js
@@ -0,0 +1,23 @@
+function testMin(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.min(...arr);
+ }
+ return sum;
+}
+
+function testMax(arr) {
+ var sum = 0;
+ for (var i = 0; i < 50; i++) {
+ sum += Math.max(...arr);
+ }
+ return sum;
+}
+
+// Attach Int32MinMaxArrayResult.
+assertEq(testMin([1,2,3,4,5]), 50);
+assertEq(testMax([1,2,3,4,5]), 250);
+
+// Verify that we handle a double element correctly.
+assertEq(testMin([1,2,3.5,4,5]), 50);
+assertEq(testMax([1,2,3.5,4,5]), 250);
diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-3.js b/js/src/jit-test/tests/cacheir/spread-minmax-3.js
new file mode 100644
index 0000000000..ae92e5e555
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/spread-minmax-3.js
@@ -0,0 +1,25 @@
+function testMin(arr) {
+ return Math.min(...arr);
+}
+
+function testMax(arr) {
+ return Math.max(...arr);
+}
+
+with({}) {}
+
+// Warp-compile.
+var sum = 0;
+for (var i = 0; i < 50; i++) {
+ sum += testMin([1, 2.5, 3]);
+ sum += testMax([1, 2.5, 3]);
+}
+assertEq(sum, 200);
+
+// Test min/max with no arguments.
+assertEq(testMin([]), Infinity);
+assertEq(testMax([]), -Infinity);
+
+// Test NaN.
+assertEq(testMin([1,NaN]), NaN);
+assertEq(testMax([1,NaN]), NaN);
diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-4.js b/js/src/jit-test/tests/cacheir/spread-minmax-4.js
new file mode 100644
index 0000000000..b6e57b9550
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/spread-minmax-4.js
@@ -0,0 +1,15 @@
+function f(...rest) {
+ for (var i = 0; i < 100; ++i) {
+ rest[0] = 0;
+ var v = Math.max(...rest);
+
+ rest[0] = i;
+ var w = Math.max(...rest);
+
+ assertEq(v, 0);
+ assertEq(w, i);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ f(0, 0);
+}
diff --git a/js/src/jit-test/tests/cacheir/spread-minmax-5.js b/js/src/jit-test/tests/cacheir/spread-minmax-5.js
new file mode 100644
index 0000000000..a787f16783
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/spread-minmax-5.js
@@ -0,0 +1,12 @@
+function f() {
+ var arr = [[]];
+ for (var i = 1; i < 101; i++) {
+ arr[0].push(i);
+ }
+ var res = 0;
+ for (var i = 0; i < 100; i++) {
+ res += Math.max(...arr[0]) + Math.min(...arr[0]);
+ }
+ assertEq(res, 10100);
+}
+f();
diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js
new file mode 100644
index 0000000000..21ad5f5164
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-extensible.js
@@ -0,0 +1,55 @@
+// Store an element into a previous hole value and later add more elements
+// exceeding the initialised length. Cover both mono- and polymorphic call
+// sites. Change array length to non-extensible during execution.
+
+function testStoreDenseHole() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testStoreDenseHole();
+
+function testStoreDenseHolePoly() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.preventExtensions(array);
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testStoreDenseHolePoly();
diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js
new file mode 100644
index 0000000000..513fd6bc08
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length-at-start.js
@@ -0,0 +1,55 @@
+// Store an element into a previous hole value and later add more elements
+// exceeding the initialised length. Cover both mono- and polymorphic call
+// sites. The array has a non-writable length at the start.
+
+function testStoreDenseHole() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+ Object.defineProperty(array, "length", {
+ writable: false
+ });
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ store(array, i);
+ }
+
+ assertEq(array.length, 4);
+ for (var i = 0; i < 4; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 4; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testStoreDenseHole();
+
+function testStoreDenseHolePoly() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+ Object.defineProperty(array, "length", {
+ writable: false
+ });
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 4);
+ for (var i = 0; i < 4; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 4; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testStoreDenseHolePoly();
diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js
new file mode 100644
index 0000000000..468976332f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole-non-writable-length.js
@@ -0,0 +1,55 @@
+// Store an element into a previous hole value and later add more elements
+// exceeding the initialised length. Cover both mono- and polymorphic call
+// sites. Change array length to non-writable during execution.
+
+function testStoreDenseHole() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ store(array, i);
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testStoreDenseHole();
+
+function testStoreDenseHolePoly() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ if (i === 5) {
+ Object.defineProperty(array, "length", {writable: false});
+ }
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 5);
+ for (var i = 0; i < 5; ++i) {
+ assertEq(array[i], i);
+ }
+ for (var i = 5; i < 10; ++i) {
+ assertEq(i in array, false);
+ }
+}
+testStoreDenseHolePoly();
diff --git a/js/src/jit-test/tests/cacheir/store-dense-element-hole.js b/js/src/jit-test/tests/cacheir/store-dense-element-hole.js
new file mode 100644
index 0000000000..fece420056
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-dense-element-hole.js
@@ -0,0 +1,43 @@
+// Store an element into a previous hole value and later add more elements
+// exceeding the initialised length. Cover both mono- and polymorphic call
+// sites.
+
+function testStoreDenseHole() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ for (var i = 0; i < 10; ++i) {
+ store(array, i);
+ }
+
+ assertEq(array.length, 10);
+ for (var i = 0; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testStoreDenseHole();
+
+function testStoreDenseHolePoly() {
+ var array = [/* hole */, /* hole */, /* hole */, /* hole */, ];
+
+ function store(ar, index) {
+ ar[index] = index;
+ }
+
+ var objects = [array, {}];
+
+ for (var i = 0; i < 10; ++i) {
+ for (var j = 0; j < objects.length; ++j) {
+ store(objects[j], i);
+ }
+ }
+
+ assertEq(array.length, 10);
+ for (var i = 0; i < 10; ++i) {
+ assertEq(array[i], i);
+ }
+}
+testStoreDenseHolePoly();
diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-bigint.js b/js/src/jit-test/tests/cacheir/store-typed-element-bigint.js
new file mode 100644
index 0000000000..e46ca13f85
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-typed-element-bigint.js
@@ -0,0 +1,131 @@
+// Different typed array types to ensure we emit a SetProp IC.
+var xs = [
+ new BigInt64Array(10),
+ new BigUint64Array(10),
+];
+
+// Store with 0n as rhs.
+function storeConstantZero() {
+ var value = 0n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeConstantZero();
+
+// Store non-negative BigInt using inline digits.
+function storeInlineDigits() {
+ var value = 1n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeInlineDigits();
+
+// Store negative BigInt using inline digits.
+function storeInlineDigitsNegative() {
+ var value = -1n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeInlineDigitsNegative();
+
+// Still inline digits, but now two digits on 32-bit platforms
+function storeInlineDigitsTwoDigits() {
+ var value = 4294967296n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeInlineDigitsTwoDigits();
+
+// Negative case of |storeInlineDigitsTwoDigits|.
+function storeInlineDigitsTwoDigitsNegative() {
+ var value = -4294967296n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeInlineDigitsTwoDigitsNegative();
+
+// Store BigInt using heap digits.
+function storeHeapDigits() {
+ var value = 2n ** 1000n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeHeapDigits();
+
+// Store negative BigInt using heap digits.
+function storeHeapDigitsNegative() {
+ var value = -(2n ** 1000n);
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeHeapDigitsNegative();
+
+// Store BigInt with first number requiring heap digits.
+function storeFirstHeapDigits() {
+ var value = 2n ** 64n;
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeFirstHeapDigits();
+
+// Store negative BigInt with first number requiring heap digits.
+function storeFirstHeapDigitsNegative() {
+ var value = -(2n ** 64n);
+
+ for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+ ta[0] = value;
+ }
+
+ assertEq(xs[0][0], BigInt.asIntN(64, value));
+ assertEq(xs[1][0], BigInt.asUintN(64, value));
+}
+storeFirstHeapDigitsNegative();
diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js b/js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js
new file mode 100644
index 0000000000..a084a733f9
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-typed-element-constant-double-rhs.js
@@ -0,0 +1,15 @@
+// Different typed array types to ensure we emit a SetProp IC.
+var xs = [
+ new Float32Array(10),
+ new Float64Array(10),
+];
+
+for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+
+ // Store with constant rhs.
+ ta[0] = 0.1;
+}
+
+assertEq(xs[0][0], Math.fround(0.1));
+assertEq(xs[1][0], 0.1);
diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js b/js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js
new file mode 100644
index 0000000000..948fc74783
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-typed-element-payload-reg-rhs.js
@@ -0,0 +1,17 @@
+// Different typed array types to ensure we emit a SetProp IC.
+var xs = [
+ new Float32Array(10),
+ new Float64Array(10),
+];
+
+for (var i = 0; i < 100; ++i) {
+ var ta = xs[i & 1];
+
+ var v = +ta[0];
+
+ // Store with payload-register rhs.
+ ta[0] = ~v;
+}
+
+assertEq(xs[0][0], 0);
+assertEq(xs[1][0], 0);
diff --git a/js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js b/js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js
new file mode 100644
index 0000000000..dcaa059864
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/store-typed-element-payload-stack-rhs.js
@@ -0,0 +1,20 @@
+// Different typed array types to ensure we emit a SetProp IC.
+var xs = [
+ new Float32Array(10),
+ new Float64Array(10),
+];
+
+function f(ta) {
+ for (var k = 0;;) {
+ // Store with payload-stack rhs.
+ ta[k] = k;
+ break;
+ }
+}
+
+for (var i = 0; i < 100; ++i) {
+ f(xs[i & 1]);
+}
+
+assertEq(xs[0][0], 0);
+assertEq(xs[1][0], 0);
diff --git a/js/src/jit-test/tests/cacheir/string-at-oob.js b/js/src/jit-test/tests/cacheir/string-at-oob.js
new file mode 100644
index 0000000000..b7e71db87f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-at-oob.js
@@ -0,0 +1,151 @@
+// Test String.prototype.at with out-of-bounds indices.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const empty = [];
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+function codePoints() {
+ return [empty, ascii, latin1, twoByte];
+}
+
+function toRope(s) {
+ // Ropes have at least two characters.
+ if (s.length < 2) {
+ return s;
+ }
+ if (s.length === 2) {
+ return newRope(s[0], s[1]);
+ }
+ return newRope(s[0], s.substring(1));
+}
+
+function makeStrings() {
+ let strings = codePoints()
+ .map(codePoints => String.fromCodePoint(...codePoints))
+ .flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+ return strings;
+}
+
+function testNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.at(-1000);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexConstant();
+}
+
+function testNegativeIndexVariable() {
+ let indices = [-1000, -2000];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.at(indices[i & 1]);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexVariable();
+}
+
+function testNegativeOrValidIndex() {
+ let indices = [-1000, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.at(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : undefined));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeOrValidIndex();
+}
+
+function testTooLargeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.at(1000);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexConstant();
+}
+
+function testTooLargeIndexVariable() {
+ let indices = [1000, 2000];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.at(indices[i & 1]);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexVariable();
+}
+
+function testTooLargeOrValidIndex() {
+ let indices = [1000, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.at(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : undefined));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeOrValidIndex();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-at-rope.js b/js/src/jit-test/tests/cacheir/string-at-rope.js
new file mode 100644
index 0000000000..aa41fa1440
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-at-rope.js
@@ -0,0 +1,80 @@
+function makeRope() {
+ var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_");
+ var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~");
+ var rope = newRope(left, right);
+ return {left, right, rope};
+}
+
+// Load a character from the left rope child using a constant index. The input
+// to String.prototype.at is always rope.
+function testLeftChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.at(0);
+ assertEq(ch, "@");
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildConstant();
+}
+
+// Load a character from the right rope child using a constant index. The input
+// to String.prototype.at is always rope.
+function testRightChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.at(32);
+ assertEq(ch, "`");
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildConstant();
+}
+
+// Load a character from the left rope child using a variable index. The input
+// to String.prototype.at is always rope.
+function testLeftChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, rope} = makeRope();
+
+ var idx = i % left.length;
+ var ch = rope.at(idx);
+ assertEq(ch, String.fromCharCode(0x40 + idx));
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildVariable();
+}
+
+// Load a character from the right rope child using a variable index. The input
+// to String.prototype.at is always rope.
+function testRightChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, right, rope} = makeRope();
+
+ var idx = i % right.length;
+ var ch = rope.at(left.length + idx);
+ assertEq(ch, String.fromCharCode(0x60 + idx));
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildVariable();
+}
+
+// Load all characters from both child ropes. This covers the case when the
+// call to String.prototype.at linearizes the rope.
+function testBothChildren() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ for (var j = 0; j < rope.length; ++j) {
+ var ch = rope.at(j);
+ assertEq(ch, String.fromCharCode(0x40 + j));
+ }
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testBothChildren();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-at.js b/js/src/jit-test/tests/cacheir/string-at.js
new file mode 100644
index 0000000000..8dfb2f779e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-at.js
@@ -0,0 +1,115 @@
+// Test String.prototype.at with negative and non-negative indices.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const empty = [];
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+function codePoints() {
+ return [empty, ascii, latin1, twoByte];
+}
+
+function toRope(s) {
+ // Ropes have at least two characters.
+ if (s.length < 2) {
+ return s;
+ }
+ if (s.length === 2) {
+ return newRope(s[0], s[1]);
+ }
+ return newRope(s[0], s.substring(1));
+}
+
+function makeStrings() {
+ let strings = codePoints()
+ .map(codePoints => String.fromCodePoint(...codePoints))
+ .flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+ return strings;
+}
+
+function testNonNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = 0;
+ let ch = str.at(index);
+ let expected = str.charAt(index);
+ if (expected === "") expected = undefined;
+ assertEq(ch, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNonNegativeIndexConstant();
+}
+
+function testNonNegativeIndex() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = i & 3;
+ let ch = str.at(index);
+ let expected = str.charAt(index);
+ if (expected === "") expected = undefined;
+ assertEq(ch, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNonNegativeIndex();
+}
+
+function testNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = -1;
+ let ch = str.at(index);
+ let expected = str.charAt(str.length + index);
+ if (expected === "") expected = undefined;
+ assertEq(ch, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexConstant();
+}
+
+function testNegativeIndex() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = -(i & 3) - 1;
+ let ch = str.at(index);
+ let expected = str.charAt(str.length + index);
+ if (expected === "") expected = undefined;
+ assertEq(ch, expected);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndex();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-charAt-oob.js b/js/src/jit-test/tests/cacheir/string-charAt-oob.js
new file mode 100644
index 0000000000..4de28d691a
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-charAt-oob.js
@@ -0,0 +1,151 @@
+// Test String.prototype.charAt with out-of-bounds indices.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const empty = [];
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+function codePoints() {
+ return [empty, ascii, latin1, twoByte];
+}
+
+function toRope(s) {
+ // Ropes have at least two characters.
+ if (s.length < 2) {
+ return s;
+ }
+ if (s.length === 2) {
+ return newRope(s[0], s[1]);
+ }
+ return newRope(s[0], s.substring(1));
+}
+
+function makeStrings() {
+ let strings = codePoints()
+ .map(codePoints => String.fromCodePoint(...codePoints))
+ .flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+ return strings;
+}
+
+function testNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(-1);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexConstant();
+}
+
+function testNegativeIndexVariable() {
+ let indices = [-1, -2];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(indices[i & 1]);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexVariable();
+}
+
+function testNegativeOrValidIndex() {
+ let indices = [-1, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.charAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : ""));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeOrValidIndex();
+}
+
+function testTooLargeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(1000);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexConstant();
+}
+
+function testTooLargeIndexVariable() {
+ let indices = [1000, 2000];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(indices[i & 1]);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexVariable();
+}
+
+function testTooLargeOrValidIndex() {
+ let indices = [1000, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.charAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : ""));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeOrValidIndex();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-charAt-rope.js b/js/src/jit-test/tests/cacheir/string-charAt-rope.js
new file mode 100644
index 0000000000..ea656e132c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-charAt-rope.js
@@ -0,0 +1,80 @@
+function makeRope() {
+ var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_");
+ var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~");
+ var rope = newRope(left, right);
+ return {left, right, rope};
+}
+
+// Load a character from the left rope child using a constant index. The input
+// to String.prototype.charAt is always rope.
+function testLeftChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.charAt(0);
+ assertEq(ch, "@");
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildConstant();
+}
+
+// Load a character from the right rope child using a constant index. The input
+// to String.prototype.charAt is always rope.
+function testRightChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.charAt(32);
+ assertEq(ch, "`");
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildConstant();
+}
+
+// Load a character from the left rope child using a variable index. The input
+// to String.prototype.charAt is always rope.
+function testLeftChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, rope} = makeRope();
+
+ var idx = i % left.length;
+ var ch = rope.charAt(idx);
+ assertEq(ch, String.fromCharCode(0x40 + idx));
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildVariable();
+}
+
+// Load a character from the right rope child using a variable index. The input
+// to String.prototype.charAt is always rope.
+function testRightChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, right, rope} = makeRope();
+
+ var idx = i % right.length;
+ var ch = rope.charAt(left.length + idx);
+ assertEq(ch, String.fromCharCode(0x60 + idx));
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildVariable();
+}
+
+// Load all characters from both child ropes. This covers the case when the
+// call to String.prototype.charAt linearizes the rope.
+function testBothChildren() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ for (var j = 0; j < rope.length; ++j) {
+ var ch = rope.charAt(j);
+ assertEq(ch, String.fromCharCode(0x40 + j));
+ }
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testBothChildren();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js b/js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js
new file mode 100644
index 0000000000..da9b826d94
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-charCodeAt-oob.js
@@ -0,0 +1,151 @@
+// Test String.prototype.charCodeAt with out-of-bounds indices.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const empty = [];
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+function codePoints() {
+ return [empty, ascii, latin1, twoByte];
+}
+
+function toRope(s) {
+ // Ropes have at least two characters.
+ if (s.length < 2) {
+ return s;
+ }
+ if (s.length === 2) {
+ return newRope(s[0], s[1]);
+ }
+ return newRope(s[0], s.substring(1));
+}
+
+function makeStrings() {
+ let strings = codePoints()
+ .map(codePoints => String.fromCodePoint(...codePoints))
+ .flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+ return strings;
+}
+
+function testNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charCodeAt(-1);
+ assertEq(ch, NaN);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexConstant();
+}
+
+function testNegativeIndexVariable() {
+ let indices = [-1, -2];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charCodeAt(indices[i & 1]);
+ assertEq(ch, NaN);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexVariable();
+}
+
+function testNegativeOrValidIndex() {
+ let indices = [-1, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.charCodeAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? cp[index] : NaN));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeOrValidIndex();
+}
+
+function testTooLargeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charCodeAt(1000);
+ assertEq(ch, NaN);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexConstant();
+}
+
+function testTooLargeIndexVariable() {
+ let indices = [1000, 2000];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charCodeAt(indices[i & 1]);
+ assertEq(ch, NaN);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexVariable();
+}
+
+function testTooLargeOrValidIndex() {
+ let indices = [1000, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.charCodeAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? cp[index] : NaN));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeOrValidIndex();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js b/js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js
new file mode 100644
index 0000000000..645c023230
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-charCodeAt-rope.js
@@ -0,0 +1,80 @@
+function makeRope() {
+ var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_");
+ var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~");
+ var rope = newRope(left, right);
+ return {left, right, rope};
+}
+
+// Load a character from the left rope child using a constant index. The input
+// to String.prototype.charCodeAt is always rope.
+function testLeftChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.charCodeAt(0);
+ assertEq(ch, 0x40);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildConstant();
+}
+
+// Load a character from the right rope child using a constant index. The input
+// to String.prototype.charCodeAt is always rope.
+function testRightChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.charCodeAt(32);
+ assertEq(ch, 0x60);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildConstant();
+}
+
+// Load a character from the left rope child using a variable index. The input
+// to String.prototype.charCodeAt is always rope.
+function testLeftChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, rope} = makeRope();
+
+ var idx = i % left.length;
+ var ch = rope.charCodeAt(idx);
+ assertEq(ch, 0x40 + idx);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildVariable();
+}
+
+// Load a character from the right rope child using a variable index. The input
+// to String.prototype.charCodeAt is always rope.
+function testRightChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, right, rope} = makeRope();
+
+ var idx = i % right.length;
+ var ch = rope.charCodeAt(left.length + idx);
+ assertEq(ch, 0x60 + idx);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildVariable();
+}
+
+// Load all characters from both child ropes. This covers the case when the
+// call to String.prototype.charCodeAt linearizes the rope.
+function testBothChildren() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ for (var j = 0; j < rope.length; ++j) {
+ var ch = rope.charCodeAt(j);
+ assertEq(ch, 0x40 + j);
+ }
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testBothChildren();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-oob.js b/js/src/jit-test/tests/cacheir/string-codePointAt-oob.js
new file mode 100644
index 0000000000..f456361b5e
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-codePointAt-oob.js
@@ -0,0 +1,151 @@
+// Test String.prototype.codePointAt with out-of-bounds indices.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const empty = [];
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+function codePoints() {
+ return [empty, ascii, latin1, twoByte];
+}
+
+function toRope(s) {
+ // Ropes have at least two characters.
+ if (s.length < 2) {
+ return s;
+ }
+ if (s.length === 2) {
+ return newRope(s[0], s[1]);
+ }
+ return newRope(s[0], s.substring(1));
+}
+
+function makeStrings() {
+ let strings = codePoints()
+ .map(codePoints => String.fromCodePoint(...codePoints))
+ .flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+ return strings;
+}
+
+function testNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.codePointAt(-1);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexConstant();
+}
+
+function testNegativeIndexVariable() {
+ let indices = [-1, -2];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.codePointAt(indices[i & 1]);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexVariable();
+}
+
+function testNegativeOrValidIndex() {
+ let indices = [-1, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.codePointAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? cp[index] : undefined));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeOrValidIndex();
+}
+
+function testTooLargeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.codePointAt(1000);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexConstant();
+}
+
+function testTooLargeIndexVariable() {
+ let indices = [1000, 2000];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.codePointAt(indices[i & 1]);
+ assertEq(ch, undefined);
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexVariable();
+}
+
+function testTooLargeOrValidIndex() {
+ let indices = [1000, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.codePointAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? cp[index] : undefined));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeOrValidIndex();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js b/js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js
new file mode 100644
index 0000000000..adbc9995c1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-codePointAt-rope-twobyte.js
@@ -0,0 +1,88 @@
+function makeString(from) {
+ var codePoints = [];
+ for (var i = 0; i < 16; ++i) {
+ codePoints.push(from + i);
+ }
+ return String.fromCodePoint(...codePoints);
+}
+
+function makeRope() {
+ var left = newRope(makeString(0x140), makeString(0x140 + 16));
+ var right = newRope(makeString(0x160), makeString(0x160 + 16));
+ var rope = newRope(left, right);
+ return {left, right, rope};
+}
+
+// Load a character from the left rope child using a constant index. The input
+// to String.prototype.codePointAt is always rope.
+function testLeftChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.codePointAt(0);
+ assertEq(ch, 0x140);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildConstant();
+}
+
+// Load a character from the right rope child using a constant index. The input
+// to String.prototype.codePointAt is always rope.
+function testRightChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.codePointAt(32);
+ assertEq(ch, 0x160);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildConstant();
+}
+
+// Load a character from the left rope child using a variable index. The input
+// to String.prototype.codePointAt is always rope.
+function testLeftChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, rope} = makeRope();
+
+ var idx = i % left.length;
+ var ch = rope.codePointAt(idx);
+ assertEq(ch, 0x140 + idx);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildVariable();
+}
+
+// Load a character from the right rope child using a variable index. The input
+// to String.prototype.codePointAt is always rope.
+function testRightChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, right, rope} = makeRope();
+
+ var idx = i % right.length;
+ var ch = rope.codePointAt(left.length + idx);
+ assertEq(ch, 0x160 + idx);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildVariable();
+}
+
+// Load all characters from both child ropes. This covers the case when the
+// call to String.prototype.codePointAt linearizes the rope.
+function testBothChildren() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ for (var j = 0; j < rope.length; ++j) {
+ var ch = rope.codePointAt(j);
+ assertEq(ch, 0x140 + j);
+ }
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testBothChildren();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-rope.js b/js/src/jit-test/tests/cacheir/string-codePointAt-rope.js
new file mode 100644
index 0000000000..76e16c7ea3
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-codePointAt-rope.js
@@ -0,0 +1,80 @@
+function makeRope() {
+ var left = newRope("@ABCDEFGHIJKLMNO", "PQRSTUVWXYZ[\\]^_");
+ var right = newRope("`abcdefghijklmno", "pqrstuvwxyz{|}~");
+ var rope = newRope(left, right);
+ return {left, right, rope};
+}
+
+// Load a character from the left rope child using a constant index. The input
+// to String.prototype.codePointAt is always rope.
+function testLeftChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.codePointAt(0);
+ assertEq(ch, 0x40);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildConstant();
+}
+
+// Load a character from the right rope child using a constant index. The input
+// to String.prototype.codePointAt is always rope.
+function testRightChildConstant() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ var ch = rope.codePointAt(32);
+ assertEq(ch, 0x60);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildConstant();
+}
+
+// Load a character from the left rope child using a variable index. The input
+// to String.prototype.codePointAt is always rope.
+function testLeftChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, rope} = makeRope();
+
+ var idx = i % left.length;
+ var ch = rope.codePointAt(idx);
+ assertEq(ch, 0x40 + idx);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLeftChildVariable();
+}
+
+// Load a character from the right rope child using a variable index. The input
+// to String.prototype.codePointAt is always rope.
+function testRightChildVariable() {
+ for (var i = 0; i < 200; ++i) {
+ var {left, right, rope} = makeRope();
+
+ var idx = i % right.length;
+ var ch = rope.codePointAt(left.length + idx);
+ assertEq(ch, 0x60 + idx);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRightChildVariable();
+}
+
+// Load all characters from both child ropes. This covers the case when the
+// call to String.prototype.codePointAt linearizes the rope.
+function testBothChildren() {
+ for (var i = 0; i < 200; ++i) {
+ var {rope} = makeRope();
+
+ for (var j = 0; j < rope.length; ++j) {
+ var ch = rope.codePointAt(j);
+ assertEq(ch, 0x40 + j);
+ }
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testBothChildren();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js b/js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js
new file mode 100644
index 0000000000..961953a598
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-codePointAt-surrogate.js
@@ -0,0 +1,103 @@
+function testLinear() {
+ var s = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}";
+
+ for (var i = 0; i < 200; ++i) {
+ // Iterate over all possible string indices, which includes reading
+ // unpaired lead surrogates.
+ var index = i % s.length;
+
+ var c = s.codePointAt(index);
+ var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1);
+ assertEq(c, e);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLinear();
+}
+
+function testLinearOnlyValidCodePoints() {
+ var s = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}";
+
+ for (var i = 0; i < 200; ++i) {
+ // Iterator over all valid code point indices. (Code points are at all even
+ // string indices.)
+ var index = (i % s.length) & ~1;
+
+ var c = s.codePointAt(index);
+ var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1);
+ assertEq(c, e);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testLinearOnlyValidCodePoints();
+}
+
+function testRope() {
+ var left = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}";
+ var right = "\u{10008}\u{10009}\u{1000A}\u{1000B}\u{1000C}\u{1000D}\u{1000E}\u{1000F}";
+
+ for (var i = 0; i < 200; ++i) {
+ var s = newRope(left, right);
+
+ // Iterate over all possible string indices, which includes reading
+ // unpaired lead surrogates.
+ var index = i % s.length;
+
+ var c = s.codePointAt(index);
+ var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1);
+ assertEq(c, e);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRope();
+}
+
+function testRopeOnlyValidCodePoints() {
+ var left = "\u{10000}\u{10001}\u{10002}\u{10003}\u{10004}\u{10005}\u{10006}\u{10007}";
+ var right = "\u{10008}\u{10009}\u{1000A}\u{1000B}\u{1000C}\u{1000D}\u{1000E}\u{1000F}";
+
+ for (var i = 0; i < 200; ++i) {
+ var s = newRope(left, right);
+
+ // Iterator over all valid code point indices. (Code points are at all even
+ // string indices.)
+ var index = (i % s.length) & ~1;
+
+ var c = s.codePointAt(index);
+ var e = ((index & 1) ? 0xdc00 : 0x10000) + (index >> 1);
+ assertEq(c, e);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testRopeOnlyValidCodePoints();
+}
+
+function testUnpairedLead() {
+ var s = "\u{d800}\u{d801}\u{d802}\u{d803}\u{d804}\u{d805}\u{d806}\u{d807}";
+
+ for (var i = 0; i < 200; ++i) {
+ var index = i % s.length;
+
+ var c = s.codePointAt(index);
+ var e = 0xd800 + index;
+ assertEq(c, e);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testUnpairedLead();
+}
+
+function testUnpairedTail() {
+ var s = "\u{dc00}\u{dc01}\u{dc02}\u{dc03}\u{dc04}\u{dc05}\u{dc06}\u{dc07}";
+
+ for (var i = 0; i < 200; ++i) {
+ var index = i % s.length;
+
+ var c = s.codePointAt(index);
+ var e = 0xdc00 + index;
+ assertEq(c, e);
+ }
+}
+for (var i = 0; i < 2; ++i) {
+ testUnpairedTail();
+}
diff --git a/js/src/jit-test/tests/cacheir/string-concat-null-undef.js b/js/src/jit-test/tests/cacheir/string-concat-null-undef.js
new file mode 100644
index 0000000000..6ef75dc181
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-concat-null-undef.js
@@ -0,0 +1,31 @@
+function test() {
+ var nullUndefLhs = [
+ [null, "foo", "nullfoo"],
+ [null, "bar", "nullbar"],
+ [null, "123", "null123"],
+ [null, "", "null"],
+ [undefined, "abc", "undefinedabc"],
+ [undefined, "1", "undefined1"],
+ [undefined, "", "undefined"],
+ ["abc", "def", "abcdef"],
+ ];
+ for (var [lhs, rhs, expected] of nullUndefLhs) {
+ assertEq(lhs + rhs, expected);
+ }
+ var nullUndefRhs = [
+ ["foo", undefined, "fooundefined"],
+ ["bar", undefined, "barundefined"],
+ ["123", undefined, "123undefined"],
+ ["", undefined, "undefined"],
+ ["abc", null, "abcnull"],
+ ["1", null, "1null"],
+ ["", null, "null"],
+ ["abc", "def", "abcdef"],
+ ];
+ for (var [lhs, rhs, expected] of nullUndefRhs) {
+ assertEq(lhs + rhs, expected);
+ }
+}
+test();
+test();
+test();
diff --git a/js/src/jit-test/tests/cacheir/string-fromCharCode-double.js b/js/src/jit-test/tests/cacheir/string-fromCharCode-double.js
new file mode 100644
index 0000000000..4db1787148
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-fromCharCode-double.js
@@ -0,0 +1,17 @@
+// Test inlining String.fromCharCode with Double instead of Int32 inputs.
+
+function test() {
+ for (let i = 0; i < 0xffff; ++i) {
+ let expected = String.fromCharCode(i);
+
+ // Ensure the Double input is truncated to Int32.
+ assertEq(String.fromCharCode(i + 0.5), expected);
+
+ // The input is converted using ToUint16.
+ assertEq(String.fromCharCode(i + 0.5 + 0x10_000), expected);
+
+ // Test ToUint16 conversion when adding INT32_MAX + 1.
+ assertEq(String.fromCharCode(i + 0x8000_0000), expected);
+ }
+}
+for (let i = 0; i < 2; ++i) test();
diff --git a/js/src/jit-test/tests/cacheir/string-fromcodepoint.js b/js/src/jit-test/tests/cacheir/string-fromcodepoint.js
new file mode 100644
index 0000000000..254f41fd85
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-fromcodepoint.js
@@ -0,0 +1,15 @@
+function f() {
+ for (var i = 0x10F000; i <= 0x10FFFF + 1; ++i) {
+ String.fromCodePoint(i);
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ var err;
+ try {
+ f();
+ } catch (e) {
+ err = e;
+ }
+ assertEq(err instanceof RangeError, true);
+}
diff --git a/js/src/jit-test/tests/cacheir/string-int32-arith.js b/js/src/jit-test/tests/cacheir/string-int32-arith.js
new file mode 100644
index 0000000000..8f39d3e124
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-int32-arith.js
@@ -0,0 +1,56 @@
+function test(zero, one) {
+ assertEq(10 - zero, 10);
+ assertEq(10 - one, 9);
+ assertEq(zero - 0, 0);
+ assertEq(one - 1, 0);
+
+ assertEq(10 * zero, 0);
+ assertEq(zero * 10, 0);
+ assertEq(10 * one, 10);
+ assertEq(one * 10, 10);
+
+ assertEq(10 / one, 10);
+ assertEq(one / 1, 1);
+ assertEq(10 % one, 0);
+ assertEq(one % 1, 0);
+
+ assertEq(10 ** one, 10);
+ assertEq(one ** 4, 1);
+
+ assertEq(10 & zero, 0);
+ assertEq(zero & 10, 0);
+ assertEq(10 & one, 0);
+ assertEq(one & 10, 0);
+
+ assertEq(10 | zero, 10);
+ assertEq(zero | 10, 10);
+ assertEq(10 | one, 11);
+ assertEq(one | 10, 11);
+
+ assertEq(10 ^ zero, 10);
+ assertEq(zero ^ 10, 10);
+ assertEq(10 ^ one, 11);
+ assertEq(one ^ 10, 11);
+
+ assertEq(10 << zero, 10);
+ assertEq(zero << 10, 0);
+ assertEq(10 << one, 20);
+ assertEq(one << 10, 1024);
+
+ assertEq(10 >> zero, 10);
+ assertEq(zero >> 10, 0);
+ assertEq(10 >> one, 5);
+ assertEq(one >> 10, 0);
+
+ assertEq(10 >>> zero, 10);
+ assertEq(zero >>> 10, 0);
+ assertEq(10 >>> one, 5);
+ assertEq(one >>> 10, 0);
+}
+
+for (var i = 0; i < 10; i++) {
+ test(0, 1);
+ test('0', '1');
+ test('0x0', '0x1');
+ test('0.0', '1.0');
+}
diff --git a/js/src/jit-test/tests/cacheir/string-lastIndexOf.js b/js/src/jit-test/tests/cacheir/string-lastIndexOf.js
new file mode 100644
index 0000000000..f281d1eac1
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-lastIndexOf.js
@@ -0,0 +1,102 @@
+function testEmpty() {
+ var strings = [
+ "",
+ "a",
+ "ab",
+ ];
+
+ for (var i = 0; i < 200; ++i) {
+ var str = strings[i % strings.length];
+ assertEq(str.lastIndexOf(""), str.length);
+ }
+}
+testEmpty();
+
+function testSingle() {
+ var strings = [
+ "",
+
+ "a",
+ "b",
+
+ "aa",
+ "ab",
+ "ba",
+ "bb",
+ ];
+
+ var searchStrings = [
+ "a",
+ "b",
+ ];
+
+ for (var i = 0; i < 200; ++i) {
+ var str = strings[i % strings.length];
+ var searchString = searchStrings[i % searchStrings.length];
+
+ var j = str.length;
+ while (--j >= 0 && str[j] !== searchString);
+
+ assertEq(str.lastIndexOf(searchString), j);
+ }
+}
+testSingle();
+
+function testDouble() {
+ var strings = [
+ "",
+
+ "a",
+ "b",
+
+ "aa",
+ "ab",
+ "ba",
+ "bb",
+
+ "aaa",
+ "aab",
+ "aba",
+ "abb",
+ "baa",
+ "bab",
+ "bba",
+ "bbb",
+
+ "aaaa",
+ "aaab",
+ "aaba",
+ "aabb",
+ "abaa",
+ "abab",
+ "abba",
+ "abbb",
+
+ "baaa",
+ "baab",
+ "baba",
+ "babb",
+ "bbaa",
+ "bbab",
+ "bbba",
+ "bbbb",
+ ];
+
+ var searchStrings = [
+ "aa",
+ "ab",
+ "ba",
+ "bb",
+ ];
+
+ for (var i = 0; i < 200; ++i) {
+ var str = strings[i % strings.length];
+ var searchString = searchStrings[i % searchStrings.length];
+
+ var j = str.length;
+ while (--j >= 0 && (str[j] !== searchString[0] || str[j + 1] !== searchString[1]));
+
+ assertEq(str.lastIndexOf(searchString), j);
+ }
+}
+testDouble();
diff --git a/js/src/jit-test/tests/cacheir/string-loadchar.js b/js/src/jit-test/tests/cacheir/string-loadchar.js
new file mode 100644
index 0000000000..f89c1d8b33
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-loadchar.js
@@ -0,0 +1,44 @@
+// Load Latin-1 and Two-Byte strings.
+function latin1AndTwoByte() {
+ for (var i = 0; i <= 0x03FF; ++i) {
+ var s = String.fromCodePoint(i);
+ assertEq(s[0], s);
+ assertEq(s.charAt(0), s);
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ latin1AndTwoByte();
+}
+
+// Test bi-morphic loads.
+function stringAndArray() {
+ var xs = [["\u0100"], "\u0100"];
+ for (var i = 0; i < 200; ++i) {
+ var x = xs[i & 1];
+ var s = x[0];
+ assertEq(s.length, 1);
+ assertEq(s, "\u0100");
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ stringAndArray();
+}
+
+function outOfBoundsFailureThenException() {
+ var name = "Object";
+
+ var j = 0;
+ while (true) {
+ // Access out-of-bounds character and then trigger an exception,
+ // when accessing a property on the undefined value.
+ name[j++].does_not_exist;
+ }
+}
+
+for (var i = 0; i < 2; ++i) {
+ try {
+ outOfBoundsFailureThenException();
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/cacheir/string-number-arith.js b/js/src/jit-test/tests/cacheir/string-number-arith.js
new file mode 100644
index 0000000000..ec1caf76ff
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-number-arith.js
@@ -0,0 +1,32 @@
+function test(half, minusOneHalf) {
+ assertEq(10 - half, 9.5);
+ assertEq(10 - minusOneHalf, 11.5);
+ assertEq(half - 0, 0.5);
+ assertEq(minusOneHalf - 1, -2.5);
+
+ assertEq(10 * half, 5);
+ assertEq(half * 10, 5);
+ assertEq(10 * minusOneHalf, -15);
+ assertEq(minusOneHalf * 10, -15);
+
+ assertEq(10 / half, 20);
+ assertEq(half / 1, 0.5);
+ assertEq(12 / minusOneHalf, -8);
+ assertEq(minusOneHalf / 1, -1.5);
+
+ assertEq(10 % half, 0);
+ assertEq(half % 1, 0.5);
+ assertEq(12 % minusOneHalf, 0);
+ assertEq(minusOneHalf % 1, -0.5);
+
+ assertEq(10 ** half, Math.sqrt(10));
+ assertEq(half ** 4, 0.0625);
+ assertEq(16 ** minusOneHalf, 0.015625);
+ assertEq(minusOneHalf ** 4, 5.0625);
+}
+
+for (var i = 0; i < 10; i++) {
+ test(0.5, -1.5);
+ test("0.5", "-1.5");
+ test("5e-1", "-15e-1");
+}
diff --git a/js/src/jit-test/tests/cacheir/string-toString-valueOf.js b/js/src/jit-test/tests/cacheir/string-toString-valueOf.js
new file mode 100644
index 0000000000..af50c50256
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-toString-valueOf.js
@@ -0,0 +1,23 @@
+function simple(str) {
+ assertEq(str.toString(), "abc");
+ assertEq(str.valueOf(), "abc");
+}
+
+function obj(str) {
+ var obj = new String(str);
+ assertEq(obj.toString(), "xyz");
+ assertEq(obj.valueOf(), "xyz");
+}
+
+function mixed() {
+ for (var v of ["abc", new String("abc")]) {
+ assertEq(v.toString(), "abc");
+ assertEq(v.valueOf(), "abc");
+ }
+}
+
+for (var i = 0; i < 100; i++) {
+ simple("abc");
+ obj("xyz");
+ mixed();
+} \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/stub-fold-closeiter.js b/js/src/jit-test/tests/cacheir/stub-fold-closeiter.js
new file mode 100644
index 0000000000..8e91d3737c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/stub-fold-closeiter.js
@@ -0,0 +1,25 @@
+class Iterator {
+ val = 0;
+ next() {
+ return { value: this.val++, done: false }
+ }
+ return() { return { value: undefined, done: true }}
+}
+
+var arr = [];
+for (var i = 0; i < 10; i++) {
+ class SubIter extends Iterator {}
+ var iterable = {
+ [Symbol.iterator]() { return new SubIter(); }
+ }
+ arr.push(iterable);
+}
+
+function foo() {
+ for (var x of arr[i % arr.length]) {
+ if (x > 1) { return; }
+ }
+}
+
+with ({}) {}
+for (var i = 0; i < 100; i++) { foo(); }
diff --git a/js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js b/js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js
new file mode 100644
index 0000000000..b1232c2e2c
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/symbol-loose-equal-incompatible.js
@@ -0,0 +1,35 @@
+// Test loose equality comparison between Symbols and String/Boolean/Int32/Double/BigInt.
+
+var xs = [
+ Symbol(), Symbol(), Symbol(), Symbol(),
+ Symbol(), Symbol(), Symbol(), Symbol(),
+];
+
+var ys = [
+ "", "test", true, false,
+ 123, 123.5, NaN, 456n,
+];
+
+function testLooseEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ assertEq(x == y, false);
+ assertEq(y == x, false);
+ }
+}
+testLooseEqual();
+
+function testLooseNotEqual() {
+ for (var i = 0; i < 100; ++i) {
+ var j = i % xs.length;
+ var x = xs[j];
+ var y = ys[j];
+
+ assertEq(x != y, true);
+ assertEq(y != x, true);
+ }
+}
+testLooseNotEqual();
diff --git a/js/src/jit-test/tests/cacheir/tobool.js b/js/src/jit-test/tests/cacheir/tobool.js
new file mode 100644
index 0000000000..8487703e1f
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/tobool.js
@@ -0,0 +1,95 @@
+var success = 0;
+var expected_bool = 0;
+var expected_int = 0;
+var expected_double = 0;
+var expected_string = 0;
+var expected_object = 0;
+var expected_symbol = 0;
+
+function test_type_stable_ic() {
+ // Initialize as falsy where possible.
+ var a = undefined; // No Change, never succeeds
+ var b = null; // No Change, never succeeds
+ var c = false; // Alternate between true and false.
+ var d = 0; // Int32 cache checker, change int values
+ var e = ""; // String cache checker, change string values
+ var f = Symbol(); // No change, always succeed, no cache.
+ var g = {}; // Change objects, always succeed.
+ var h = -0; // Double cache checker, change double values.
+
+ for (var i =0; i < 30; i++) {
+ // Switch between values to ensure the caches fire.
+ switch (i % 3) {
+ case 0:
+ d = 0;
+ e = "hi"; expected_string++;
+ c = true; expected_bool++;
+ h = 0;
+ break;
+ case 1:
+ d = 1; expected_int++;
+ e = "";
+ c = false;
+ h = NaN;
+ break;
+ case 2:
+ d = 2; expected_int++;
+ h = 1.234; expected_double++;
+ g = {};
+ break;
+ }
+
+ if (a) { success++; }
+ if (b) { success++; }
+ if (c) { success++; }
+ if (d) { success++; }
+ if (e) { success++; }
+ if (f) { success++; } expected_symbol++; // Symbol succeed
+ if (g) { success++; } expected_object++; // Object success
+ if (h) { success++; }
+ }
+}
+
+test_type_stable_ic();
+
+assertEq(success, expected_bool + expected_double + expected_int + expected_object + expected_string + expected_symbol);
+
+
+// Test cache failures.
+function helper(fun, arg, n)
+{
+ var r = 0;
+ for (var i = 0; i < n; i++) {
+ r = fun(arg);
+ }
+ return r ? 1 : 0;
+}
+
+function test_transition(fun, load, test, before, after) {
+ var a = helper(fun, load, 30);
+ var x = helper(fun, test, 5)
+ assertEq(a, before);
+ assertEq(x, after)
+}
+
+var fun1 = (x) => { if (x) return true; else return false; };
+var fun2 = (x) => { if (x) return true; else return false; };
+var fun3 = (x) => { if (x) return true; else return false; };
+var fun4 = (x) => { if (x) return true; else return false; };
+var fun5 = (x) => { if (x) return true; else return false; };
+var fun6 = (x) => { if (x) return true; else return false; };
+var fun7 = (x) => { if (x) return true; else return false; };
+var fun8 = (x) => { if (x) return true; else return false; };
+
+// NaN -> Int32
+test_transition(fun1, NaN, 1, 0, 1);
+test_transition(fun2, 1, NaN, 1, 0);
+// NaN -> Object / Object -> NaN
+test_transition(fun3, NaN, {}, 0, 1);
+test_transition(fun4, {}, NaN, 1, 0);
+// Object -> null / null -> Object
+test_transition(fun5, {}, null, 1, 0);
+test_transition(fun6, null, {}, 0, 1);
+// Symbol -> null, null -> Symbol
+test_transition(fun7, Symbol('hi'), null, 1, 0);
+test_transition(fun8, null, Symbol('lo'), 0, 1); \ No newline at end of file
diff --git a/js/src/jit-test/tests/cacheir/topropertykey.js b/js/src/jit-test/tests/cacheir/topropertykey.js
new file mode 100644
index 0000000000..69f9e1be9d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/topropertykey.js
@@ -0,0 +1,36 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+function testInt32() {
+ var xs = [0, 0];
+ var a = [0];
+
+ for (var i = 0; i < 20; ++i) {
+ var key = xs[i & 1];
+ assertEq(a[key]++, i);
+ }
+}
+for (var i = 0; i < 2; ++i) testInt32();
+
+function testStringInt32() {
+ var xs = ["0", "0"];
+ var a = [0];
+
+ for (var i = 0; i < 20; ++i) {
+ var key = xs[i & 1];
+ assertEq(a[key]++, i);
+ }
+}
+for (var i = 0; i < 2; ++i) testStringInt32();
+
+function testString() {
+ var xs = ["p", "p"];
+ var a = {
+ p: 0,
+ };
+
+ for (var i = 0; i < 20; ++i) {
+ var key = xs[i & 1];
+ assertEq(a[key]++, i);
+ }
+}
+for (var i = 0; i < 2; ++i) testString();
diff --git a/js/src/jit-test/tests/cacheir/typed-array-intrinsics.js b/js/src/jit-test/tests/cacheir/typed-array-intrinsics.js
new file mode 100644
index 0000000000..788122bc79
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typed-array-intrinsics.js
@@ -0,0 +1,93 @@
+var ab = new ArrayBuffer(128);
+var uint8c = new Uint8ClampedArray(ab, 3);
+var int16 = new Int16Array(ab, 2);
+var int32 = new Int32Array(ab, 4);
+var float32 = new Float32Array(ab, 0);
+var float64 = new Float64Array(ab, 64);
+var bigInt64 = new BigInt64Array(ab, 32);
+
+var uint8cProxy = new Proxy(uint8c, {});
+var g = newGlobal({newCompartment: true});
+var uint8cWrapped = g.evaluate("new Uint8ClampedArray(10)");
+
+var TypedArrayElementSize = getSelfHostedValue("TypedArrayElementSize");
+var TypedArrayByteOffset = getSelfHostedValue("TypedArrayByteOffset");
+var IsTypedArray = getSelfHostedValue("IsTypedArray");
+var IsPossiblyWrappedTypedArray = getSelfHostedValue("IsPossiblyWrappedTypedArray");
+var TypedArrayLength = getSelfHostedValue("TypedArrayLength");
+
+function testElementSize() {
+ function getSize(ta) {
+ return TypedArrayElementSize(ta);
+ }
+ assertEq(getSize(uint8c), 1);
+ assertEq(getSize(int16), 2);
+ assertEq(getSize(int32), 4);
+ assertEq(getSize(float32), 4);
+ assertEq(getSize(float64), 8);
+ assertEq(getSize(bigInt64), 8);
+}
+
+function testByteOffset() {
+ function getOffset(ta) {
+ return TypedArrayByteOffset(ta);
+ }
+ assertEq(getOffset(uint8c), 3);
+ assertEq(getOffset(int16), 2);
+ assertEq(getOffset(int32), 4);
+ assertEq(getOffset(float32), 0);
+ assertEq(getOffset(float64), 64);
+ assertEq(getOffset(bigInt64), 32);
+}
+
+function testIsTypedArray() {
+ function isTA(obj) {
+ return IsTypedArray(obj);
+ }
+ assertEq(isTA(uint8c), true);
+ assertEq(isTA(int16), true);
+ assertEq(isTA(int32), true);
+ assertEq(isTA(float32), true);
+ assertEq(isTA(float64), true);
+ assertEq(isTA(bigInt64), true);
+ assertEq(isTA(Math), false);
+ assertEq(isTA(ab), false);
+ assertEq(isTA(uint8cProxy), false);
+ assertEq(isTA(uint8cWrapped), false);
+}
+
+function testIsPossiblyWrappedTypedArray() {
+ function isTA(obj) {
+ return IsPossiblyWrappedTypedArray(obj);
+ }
+ assertEq(isTA(uint8c), true);
+ assertEq(isTA(int16), true);
+ assertEq(isTA(int32), true);
+ assertEq(isTA(float32), true);
+ assertEq(isTA(float64), true);
+ assertEq(isTA(bigInt64), true);
+ assertEq(isTA(Math), false);
+ assertEq(isTA(ab), false);
+ assertEq(isTA(uint8cProxy), false);
+ assertEq(isTA(uint8cWrapped), true);
+}
+
+function testTypedArrayLength() {
+ function getLength(obj) {
+ return TypedArrayLength(obj);
+ }
+ assertEq(getLength(uint8c), 125);
+ assertEq(getLength(int16), 63);
+ assertEq(getLength(int32), 31);
+ assertEq(getLength(float32), 32);
+ assertEq(getLength(float64), 8);
+ assertEq(getLength(bigInt64), 12);
+}
+
+for (var i = 0; i < 40; i++) {
+ testElementSize();
+ testByteOffset();
+ testIsTypedArray();
+ testIsPossiblyWrappedTypedArray();
+ testTypedArrayLength();
+}
diff --git a/js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js b/js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js
new file mode 100644
index 0000000000..5733bc4c29
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-constructor-objects.js
@@ -0,0 +1,60 @@
+function testArrayBufferThenOther() {
+ var buf = new ArrayBuffer(4);
+ var other = [1, 2];
+ for (var i = 0; i < 150; i++) {
+ var arg = i < 100 ? buf : other;
+ var ta = new Int32Array(arg);
+ assertEq(ta.length, arg === buf ? 1 : 2);
+ }
+}
+testArrayBufferThenOther();
+
+function testSharedArrayBufferThenOther() {
+ if (!this.SharedArrayBuffer) {
+ return;
+ }
+ var buf = new SharedArrayBuffer(4);
+ var other = [1, 2];
+ for (var i = 0; i < 150; i++) {
+ var arg = i < 100 ? buf : other;
+ var ta = new Int32Array(arg);
+ assertEq(ta.length, arg === buf ? 1 : 2);
+ }
+}
+testSharedArrayBufferThenOther();
+
+function testArrayThenArrayBuffer() {
+ var arr = [1, 2, 3];
+ var buf = new ArrayBuffer(5);
+ for (var i = 0; i < 150; i++) {
+ var arg = i < 100 ? arr : buf;
+ var ta = new Int8Array(arg);
+ assertEq(ta.length, arg === arr ? 3 : 5);
+ }
+}
+testArrayThenArrayBuffer();
+
+function testArrayThenSharedArrayBuffer() {
+ if (!this.SharedArrayBuffer) {
+ return;
+ }
+ var arr = [1, 2, 3];
+ var buf = new SharedArrayBuffer(5);
+ for (var i = 0; i < 150; i++) {
+ var arg = i < 100 ? arr : buf;
+ var ta = new Int8Array(arg);
+ assertEq(ta.length, arg === arr ? 3 : 5);
+ }
+}
+testArrayThenSharedArrayBuffer();
+
+function testArrayThenWrapper() {
+ var arr = [1, 2, 3];
+ var wrapper = newGlobal({newCompartment: true}).evaluate("[1, 2]");
+ for (var i = 0; i < 150; i++) {
+ var arg = i < 100 ? arr : wrapper;
+ var ta = new Int8Array(arg);
+ assertEq(ta.length, arg === arr ? 3 : 2);
+ }
+}
+testArrayThenWrapper();
diff --git a/js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js
new file mode 100644
index 0000000000..e6404ae6e7
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-get.js
@@ -0,0 +1,53 @@
+function get(k) {
+ var p = {};
+ p[k] = 1;
+
+ // TypedArrays intercept any TypedArray indices (canonical numeric indices in the spec),
+ // so when reading |k| from |ta| we shouldn't return the inherited property |p[k]|.
+ var ta = new Int32Array(10);
+ Object.setPrototypeOf(ta, p);
+
+ // Assume sixteen different objects trigger a transition to a megamorphic IC.
+ var xs = [
+ ta,
+
+ {a:0}, {b:0}, {c:0}, {d:0}, {e:0}, {f:0}, {g:0}, {h:0},
+ {j:0}, {k:0}, {l:0}, {m:0}, {n:0}, {o:0}, {p:0},
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i & 15];
+ assertEq(x[k], undefined);
+ }
+}
+
+// Make sure we use a distinct function for each test.
+function test(fn) {
+ return Function(`return ${fn};`)();
+}
+
+// TypedArray index representable as an Int32.
+test(get)(100);
+test(get)("100");
+
+// TypedArray index not representable as an Int32.
+test(get)(4294967296);
+test(get)("4294967296");
+
+// Non-finite TypedArray indices.
+test(get)(Infinity);
+test(get)("Infinity");
+
+test(get)(-Infinity);
+test(get)("-Infinity");
+
+test(get)(NaN);
+test(get)("NaN");
+
+// TypedArray index with fractional parts.
+test(get)(1.1);
+test(get)("1.1");
+
+// TypedArray index with exponent parts.
+test(get)(1e+25);
+test(get)("1e+25");
diff --git a/js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js
new file mode 100644
index 0000000000..e2348caca9
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-megamorphic-has.js
@@ -0,0 +1,53 @@
+function has(k) {
+ var p = {};
+ p[k] = 1;
+
+ // TypedArrays intercept any TypedArray indices (canonical numeric indices in the spec),
+ // so when reading |k| from |ta| we shouldn't return the inherited property |p[k]|.
+ var ta = new Int32Array(10);
+ Object.setPrototypeOf(ta, p);
+
+ // Assume sixteen different objects trigger a transition to a megamorphic IC.
+ var xs = [
+ ta,
+
+ {a:0}, {b:0}, {c:0}, {d:0}, {e:0}, {f:0}, {g:0}, {h:0},
+ {j:0}, {k:0}, {l:0}, {m:0}, {n:0}, {o:0}, {p:0},
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i & 15];
+ assertEq(k in x, false);
+ }
+}
+
+// Make sure we use a distinct function for each test.
+function test(fn) {
+ return Function(`return ${fn};`)();
+}
+
+// TypedArray index representable as an Int32.
+test(has)(100);
+test(has)("100");
+
+// TypedArray index not representable as an Int32.
+test(has)(4294967296);
+test(has)("4294967296");
+
+// Non-finite TypedArray indices.
+test(has)(Infinity);
+test(has)("Infinity");
+
+test(has)(-Infinity);
+test(has)("-Infinity");
+
+test(has)(NaN);
+test(has)("NaN");
+
+// TypedArray index with fractional parts.
+test(has)(1.1);
+test(has)("1.1");
+
+// TypedArray index with exponent parts.
+test(has)(1e+25);
+test(has)("1e+25");
diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js
new file mode 100644
index 0000000000..5499c26152
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-get.js
@@ -0,0 +1,37 @@
+function get(k) {
+ // Different typed array types to ensure we emit a GetProp IC.
+ var xs = [
+ new Int32Array(10),
+ new Int16Array(10),
+ ];
+
+ // Doesn't read values from the prototype chain.
+ Object.prototype[k] = 1;
+
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i & 1];
+ assertEq(x[k], undefined);
+ }
+}
+
+// Make sure we use a distinct function for each test.
+function test(fn) {
+ return Function(`return ${fn};`)();
+}
+
+// Negative numbers values aren't int32 indices.
+test(get)(-1);
+test(get)(-2147483648);
+test(get)(-2147483649);
+
+// Int32 indices must be less-or-equal to max-int32.
+test(get)(2147483648);
+
+// Int32 indices must not have fractional parts.
+test(get)(1.5);
+test(get)(-1.5);
+
+// Non-finite numbers aren't int32 indices.
+test(get)(NaN);
+test(get)(-Infinity);
+test(get)(Infinity);
diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js
new file mode 100644
index 0000000000..47eda321ad
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-has.js
@@ -0,0 +1,37 @@
+function has(k) {
+ // Different typed array types to ensure we emit a HasProp IC.
+ var xs = [
+ new Int32Array(10),
+ new Int16Array(10),
+ ];
+
+ // Doesn't read values from the prototype chain.
+ Object.prototype[k] = 1;
+
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i & 1];
+ assertEq(k in x, false);
+ }
+}
+
+// Make sure we use a distinct function for each test.
+function test(fn) {
+ return Function(`return ${fn};`)();
+}
+
+// Negative numbers values aren't int32 indices.
+test(has)(-1);
+test(has)(-2147483648);
+test(has)(-2147483649);
+
+// Int32 indices must be less-or-equal to max-int32.
+test(has)(2147483648);
+
+// Int32 indices must not have fractional parts.
+test(has)(1.5);
+test(has)(-1.5);
+
+// Non-finite numbers aren't int32 indices.
+test(has)(NaN);
+test(has)(-Infinity);
+test(has)(Infinity);
diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js
new file mode 100644
index 0000000000..c3a6637516
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-non-int32-index-set.js
@@ -0,0 +1,37 @@
+function set(k) {
+ // Different typed array types to ensure we emit a SetProp IC.
+ var xs = [
+ new Int32Array(10),
+ new Int16Array(10),
+ ];
+
+ for (var i = 0; i < 100; ++i) {
+ var x = xs[i & 1];
+ x[k] = 0;
+ }
+
+ assertEq(xs[0][k], undefined);
+ assertEq(xs[1][k], undefined);
+}
+
+// Make sure we use a distinct function for each test.
+function test(fn) {
+ return Function(`return ${fn};`)();
+}
+
+// Negative numbers values aren't int32 indices.
+test(set)(-1);
+test(set)(-2147483648);
+test(set)(-2147483649);
+
+// Int32 indices must be less-or-equal to max-int32.
+test(set)(2147483648);
+
+// Int32 indices must not have fractional parts.
+test(set)(1.5);
+test(set)(-1.5);
+
+// Non-finite numbers aren't int32 indices.
+test(set)(NaN);
+test(set)(-Infinity);
+test(set)(Infinity);
diff --git a/js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js b/js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js
new file mode 100644
index 0000000000..f0ca0c6ac8
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-non-number-value-set.js
@@ -0,0 +1,126 @@
+const types = [
+ "Int8",
+ "Int16",
+ "Int32",
+ "Uint8",
+ "Uint16",
+ "Uint32",
+ "Uint8Clamped",
+ "Float32",
+ "Float64",
+];
+
+function convert(type, value) {
+ let num = Number(value);
+ switch (type) {
+ case "Int8":
+ return ((num | 0) << 24) >> 24;
+ case "Int16":
+ return ((num | 0) << 16) >> 16;
+ case "Int32":
+ return (num | 0);
+ case "Uint8":
+ return (num >>> 0) & 0xff;
+ case "Uint16":
+ return (num >>> 0) & 0xffff;
+ case "Uint32":
+ return (num >>> 0);
+ case "Uint8Clamped": {
+ if (Number.isNaN(num)) {
+ return 0;
+ }
+ let clamped = Math.max(0, Math.min(num, 255));
+ let f = Math.floor(clamped);
+ if (clamped < f + 0.5) {
+ return f;
+ }
+ if (clamped > f + 0.5) {
+ return f + 1;
+ }
+ return f + (f & 1);
+ }
+ case "Float32":
+ return Math.fround(num);
+ case "Float64":
+ return num;
+ }
+ throw new Error();
+}
+
+
+function runTest(type, initial, values) {
+ let expected = values.map(v => convert(type, v));
+ assertEq(
+ expected.some(e => Object.is(e, initial)),
+ false,
+ "initial must be different from the expected values"
+ );
+
+ // Create a fresh function to ensure ICs are specific to a single TypedArray kind.
+ let test = Function("initial, values, expected", `
+ let ta = new ${type}Array(1);
+ for (let i = 0; i < 200; ++i) {
+ ta[0] = initial;
+ ta[0] = values[i % values.length];
+ assertEq(ta[0], expected[i % expected.length]);
+ }
+ `);
+ test(initial, values, expected);
+}
+
+const tests = [
+ // |null| is coerced to zero.
+ {
+ initial: 1,
+ values: [null],
+ },
+
+ // |undefined| is coerced to zero or NaN.
+ {
+ initial: 1,
+ values: [undefined],
+ },
+
+ // |false| is coerced to zero and |true| is coerced to one.
+ {
+ initial: 2,
+ values: [false, true],
+ },
+
+ // Strings without a fractional part.
+ {
+ initial: 42,
+ values: [
+ "0", "1", "10", "111", "128", "256", "0x7fffffff", "0xffffffff",
+ ],
+ },
+
+ // Strings without a fractional part, but a leading minus sign.
+ {
+ initial: 42,
+ values: [
+ "-0", "-1", "-10", "-111", "-128", "-256", "-2147483647", "-4294967295",
+ ],
+ },
+
+ // Strings with a fractional number part.
+ {
+ initial: 42,
+ values: [
+ "0.1", "1.2", "10.8", "111.9",
+ "-0.1", "-1.2", "-10.8", "-111.9",
+ ],
+ },
+
+ // Special values and strings not parseable as a number.
+ {
+ initial: 42,
+ values: ["Infinity", "-Infinity", "NaN", "foobar"],
+ },
+];
+
+for (let type of types) {
+ for (let {initial, values} of tests) {
+ runTest(type, initial, values);
+ }
+}
diff --git a/js/src/jit-test/tests/cacheir/typeof-proxy.js b/js/src/jit-test/tests/cacheir/typeof-proxy.js
new file mode 100644
index 0000000000..0f97da9e2d
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typeof-proxy.js
@@ -0,0 +1,19 @@
+function test() {
+ var funs = [function() {}, new Proxy(function() {}, {}), wrapWithProto(function() {}, null), new Proxy(createIsHTMLDDA(), {})];
+ var objects = [{}, new Proxy({}, {}), wrapWithProto({}, null)];
+ var undefs = [createIsHTMLDDA(), wrapWithProto(createIsHTMLDDA(), null)];
+
+ for (var fun of funs) {
+ assertEq(typeof fun, "function")
+ }
+
+ for (var obj of objects) {
+ assertEq(typeof obj, "object");
+ }
+
+ for (var undef of undefs) {
+ assertEq(typeof undef, "undefined");
+ }
+}
+
+test();
diff --git a/js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js b/js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js
new file mode 100644
index 0000000000..cb4894ea89
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/unaryarith-null-undef-bool.js
@@ -0,0 +1,26 @@
+function test(input, resPos, resNeg, resToNumeric, resInc, resDec) {
+ assertEq(+input, resPos);
+ assertEq(-input, resNeg);
+
+ var input1 = input;
+ assertEq(input1++, resToNumeric);
+ assertEq(input1, resInc);
+
+ var input2 = input;
+ assertEq(++input2, resInc);
+ assertEq(input1, resInc);
+
+ var input3 = input;
+ assertEq(input3--, resToNumeric);
+ assertEq(input3, resDec);
+
+ var input4 = input;
+ assertEq(--input4, resDec);
+ assertEq(input4, resDec);
+}
+for (var i = 0; i < 50; i++) {
+ test(null, 0, -0, 0, 1, -1);
+ test(undefined, NaN, NaN, NaN, NaN, NaN);
+ test(true, 1, -1, 1, 2, 0);
+ test(false, 0, -0, 0, 1, -1);
+}
diff --git a/js/src/jit-test/tests/cacheir/unaryarith-string.js b/js/src/jit-test/tests/cacheir/unaryarith-string.js
new file mode 100644
index 0000000000..853d8f7464
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/unaryarith-string.js
@@ -0,0 +1,58 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function warmup(fun, input_array, output_array) {
+ assertEq(output_array.length, input_array.length);
+ for (var index = 0; index < input_array.length; index++) {
+ input = input_array[index];
+ output = output_array[index];
+ for (var i = 0; i < 30; i++) {
+ var y = fun(input);
+ assertEq(y, output)
+ }
+ }
+}
+
+// Unary - operator with transition between string representations of int32 and double.
+var fun_neg_int32_double = (x) => { return -x; }
+var fun_neg_double_int32 = (x) => { return -x; }
+
+// Unary ~ operator using string representations of either int32 or double.
+var fun_bitnot_int32 = (x) => { return ~x; }
+var fun_bitnot_double = (x) => { return ~x; }
+
+// Unary ++ operator using string representations of either int32 or double.
+var fun_inc_int32 = (x) => { return ++x; }
+var fun_inc_double = (x) => { return ++x; }
+
+// Unary -- operator using string representations of either int32 or double.
+var fun_dec_int32 = (x) => { return --x; }
+var fun_dec_double = (x) => { return --x; }
+
+// Unary + operator using string representations of either int32 or double.
+var fun_pos_int32 = (x) => { return +x; }
+var fun_pos_double = (x) => { return +x; }
+
+// JSOp::ToNumeric using string representations of either int32 or double.
+var fun_tonumeric_int32 = (x) => { return x++; }
+var fun_tonumeric_double = (x) => { return x++; }
+
+warmup(fun_neg_int32_double, ["1", "2", "-3"], [-1, -2, 3]);
+warmup(fun_neg_double_int32, ["0"], [-0]);
+
+warmup(fun_neg_double_int32, ["3", "4", "-5"], [-3, -4, 5]);
+warmup(fun_neg_int32_double, ["1.2", "1.4"], [-1.2, -1.4]);
+
+warmup(fun_bitnot_int32, ["-1", "0"], [0, -1]);
+warmup(fun_bitnot_double, ["-1.0", "0.0", "1.2", "3"], [0, -1, -2, -4]);
+
+warmup(fun_inc_int32, ["-1", "0"], [0, 1]);
+warmup(fun_inc_double, ["-1.0", "0.0", "1.2", "3"], [0, 1, 2.2, 4]);
+
+warmup(fun_dec_int32, ["-1", "0"], [-2, -1]);
+warmup(fun_dec_double, ["-1.0", "0.0", "1.5", "3"], [-2, -1, 0.5, 2]);
+
+warmup(fun_pos_int32, ["-1", "0"], [-1, 0]);
+warmup(fun_pos_double, ["-1.0", "0.0", "1.2", "3"], [-1.0, 0.0, 1.2, 3]);
+
+warmup(fun_tonumeric_int32, ["-1", "0"], [-1, 0]);
+warmup(fun_tonumeric_double, ["-1.0", "0.0", "1.2", "3"], [-1.0, 0.0, 1.2, 3]);
diff --git a/js/src/jit-test/tests/cacheir/unaryarith.js b/js/src/jit-test/tests/cacheir/unaryarith.js
new file mode 100644
index 0000000000..39dda5b0ee
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/unaryarith.js
@@ -0,0 +1,62 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function warmup(fun, input_array, output_array) {
+ assertEq(output_array.length, input_array.length);
+ for (var index = 0; index < input_array.length; index++) {
+ input = input_array[index];
+ output = output_array[index];
+ for (var i = 0; i < 30; i++) {
+ var y = fun(input);
+ assertEq(y, output)
+ }
+ }
+}
+
+// Unary - operator with transition between int32 and double.
+var fun_neg_int32_double = (x) => { return -x; }
+var fun_neg_double_int32 = (x) => { return -x; }
+
+// Unary ~ operator using either int32, double, null, or undefined.
+var fun_bitnot_int32 = (x) => { return ~x; }
+var fun_bitnot_double = (x) => { return ~x; }
+var fun_bitnot_null = (x) => { return ~x; }
+var fun_bitnot_undefined = (x) => { return ~x; }
+
+// Unary ++ operator using either int32 or double.
+var fun_inc_int32 = (x) => { return ++x; }
+var fun_inc_double = (x) => { return ++x; }
+
+// Unary -- operator using either int32 or double.
+var fun_dec_int32 = (x) => { return --x; }
+var fun_dec_double = (x) => { return --x; }
+
+// Unary + operator using either int32 or double.
+var fun_pos_int32 = (x) => { return +x; }
+var fun_pos_double = (x) => { return +x; }
+
+// JSOp::ToNumeric using either int32 or double.
+var fun_tonumeric_int32 = (x) => { return x++; }
+var fun_tonumeric_double = (x) => { return x++; }
+
+warmup(fun_neg_int32_double, [1, 2], [-1, -2]);
+warmup(fun_neg_double_int32, [0], [-0]);
+
+warmup(fun_neg_double_int32, [3, 4], [-3, -4]);
+warmup(fun_neg_int32_double, [1.2, 1.4], [-1.2, -1.4]);
+
+warmup(fun_bitnot_int32, [-1, 0], [0, -1]);
+warmup(fun_bitnot_double, [-1.0, 0.0, 1.2, 3], [0, -1, -2, -4]);
+warmup(fun_bitnot_null, [null], [-1]);
+warmup(fun_bitnot_undefined, [void 0], [-1]);
+
+warmup(fun_inc_int32, [-1, 0], [0, 1]);
+warmup(fun_inc_double, [-1.0, 0.0, 1.2, 3], [0, 1, 2.2, 4]);
+
+warmup(fun_dec_int32, [-1, 0], [-2, -1]);
+warmup(fun_dec_double, [-1.0, 0.0, 1.5, 3], [-2, -1, 0.5, 2]);
+
+warmup(fun_pos_int32, [-1, 0], [-1, 0]);
+warmup(fun_pos_double, [-1.0, 0.0, 1.2, 3], [-1.0, 0.0, 1.2, 3]);
+
+warmup(fun_tonumeric_int32, [-1, 0], [-1, 0]);
+warmup(fun_tonumeric_double, [-1.0, 0.0, 1.2, 3], [-1.0, 0.0, 1.2, 3]);
diff --git a/js/src/jit-test/tests/cacheir/unboxed-element-hole.js b/js/src/jit-test/tests/cacheir/unboxed-element-hole.js
new file mode 100644
index 0000000000..c3afd13dab
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/unboxed-element-hole.js
@@ -0,0 +1,41 @@
+function noElement() {
+ for (var i = 0; i < 1e4; i++) {
+ var obj = {length: 0};
+ assertEq(obj[0], undefined);
+ }
+}
+
+function noElementCheckPrototype() {
+ for (var i = 0; i < 1e4; i++) {
+ var obj = {length: 0};
+ assertEq(obj[0], i <= 1e3 ? undefined : 1);
+ if (i == 1e3) {
+ Object.prototype[0] = 1;
+ }
+ }
+ delete Object.prototype[0];
+}
+
+function elementOnPrototype() {
+ Object.prototype[0] = 3;
+ for (var i = 0; i < 1e4; i++) {
+ var obj = {length: 0};
+ assertEq(obj[0], 3);
+ }
+ delete Object.prototype[0];
+}
+
+function checkExpando() {
+ for (var i = 0; i < 1e4; i++) {
+ var obj = {length: 0};
+ if (i >= 1e3) {
+ obj[0] = 2;
+ }
+ assertEq(obj[0], i < 1e3 ? undefined : 2);
+ }
+}
+
+noElement();
+noElementCheckPrototype();
+elementOnPrototype();
+checkExpando();
diff --git a/js/src/jit-test/tests/cacheir/windowproxy.js b/js/src/jit-test/tests/cacheir/windowproxy.js
new file mode 100644
index 0000000000..994af384e4
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/windowproxy.js
@@ -0,0 +1,32 @@
+
+var g = newGlobal({
+ sameZoneAs: this,
+ useWindowProxy: true,
+});
+
+g.evaluate(`
+ this.data = 7;
+
+ // Getter / Setter
+ Object.defineProperty(this, "prop", {
+ get: function() { return this.data; },
+ set: function(val) { this.data = val; },
+ });
+
+ // Getter / Setter ICs
+ for (var i = 0; i < 20; ++i) {
+ this.data = i;
+ assertEq(prop, i);
+ prop = i;
+ assertEq(this.prop, i);
+ this.prop = i;
+ assertEq(this.data, i);
+ }
+`);
+
+
+// CCW of WindowProxy
+for (var i = 0; i < 20; ++i) {
+ g.slot = i;
+ assertEq(g.slot, i);
+}