summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/ion/recover-objects.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/ion/recover-objects.js')
-rw-r--r--js/src/jit-test/tests/ion/recover-objects.js224
1 files changed, 224 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/ion/recover-objects.js b/js/src/jit-test/tests/ion/recover-objects.js
new file mode 100644
index 0000000000..30865433ae
--- /dev/null
+++ b/js/src/jit-test/tests/ion/recover-objects.js
@@ -0,0 +1,224 @@
+// |jit-test| --ion-pruning=on; --fast-warmup
+
+var max = 200;
+
+// Ion eager fails the test below because we have not yet created any
+// template object in baseline before running the content of the top-level
+// function.
+if (getJitCompilerOptions()["ion.warmup.trigger"] <= max - 10)
+ setJitCompilerOption("ion.warmup.trigger", max - 10);
+
+// Force Inlining heuristics to always inline the functions which have the same
+// number of use count.
+setJitCompilerOption("ion.warmup.trigger", getJitCompilerOptions()["ion.warmup.trigger"]);
+
+// This test checks that we are able to remove the getprop & setprop with scalar
+// replacement, so we should not force inline caches, as this would skip the
+// generation of getprop & setprop instructions.
+if (getJitCompilerOptions()["ion.forceinlineCaches"])
+ setJitCompilerOption("ion.forceinlineCaches", 0);
+
+// Prevent the GC from cancelling Ion compilations, when we expect them to succeed
+gczeal(0);
+
+function resumeHere() {}
+var uceFault = function (i) {
+ if (i > max - 2)
+ uceFault = function (i) { return true; };
+ return false;
+};
+
+
+// Without "use script" in the inner function, the arguments might be
+// observable.
+function inline_notSoEmpty1(a, b, c, d) {
+ // This function is not strict, so we might be able to observe its
+ // arguments, if somebody ever called fun.arguments inside it.
+ return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
+}
+var uceFault_notSoEmpty1 = eval(`(${uceFault})`.replace('uceFault', 'uceFault_notSoEmpty1'));
+function notSoEmpty1() {
+ var a = { v: i };
+ var b = { v: 1 + a.v };
+ var c = { v: 2 + b.v };
+ var d = { v: 3 + c.v };
+ var unused = { v: 4 + d.v };
+ var res = inline_notSoEmpty1(a, b, c, d);
+ if (uceFault_notSoEmpty1(i) || uceFault_notSoEmpty1(i))
+ assertEq(i, res.v);
+ // Note, that even if the arguments are observable, we are capable of
+ // optimizing these cases by executing recover instruction before the
+ // execution of the bailout. This ensures that the observed objects are
+ // allocated once and used by the unexpected observation and the bailout.
+ assertRecoveredOnBailout(a, true);
+ assertRecoveredOnBailout(b, true);
+ assertRecoveredOnBailout(c, true);
+ assertRecoveredOnBailout(d, true);
+ assertRecoveredOnBailout(unused, true);
+ // This can only be recovered on bailout iff either we have type
+ // information for the property access in the branch, or the branch is
+ // removed before scalar replacement.
+ assertRecoveredOnBailout(res, true);
+}
+
+// Check that we can recover objects with their content.
+function inline_notSoEmpty2(a, b, c, d) {
+ "use strict";
+ return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
+}
+var uceFault_notSoEmpty2 = eval(`(${uceFault})`.replace('uceFault', 'uceFault_notSoEmpty2'));
+function notSoEmpty2(i) {
+ var a = { v: i };
+ var b = { v: 1 + a.v };
+ var c = { v: 2 + b.v };
+ var d = { v: 3 + c.v };
+ var unused = { v: 4 + d.v };
+ var res = inline_notSoEmpty2(a, b, c, d);
+ if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i))
+ assertEq(i, res.v);
+ assertRecoveredOnBailout(a, true);
+ assertRecoveredOnBailout(b, true);
+ assertRecoveredOnBailout(c, true);
+ assertRecoveredOnBailout(d, true);
+ assertRecoveredOnBailout(unused, true);
+ // This can only be recovered on bailout iff either we have type
+ // information for the property access in the branch, or the branch is
+ // removed before scalar replacement.
+ assertRecoveredOnBailout(res, true);
+}
+
+// Check that we can recover objects with their content.
+var argFault_observeArg = function (i) {
+ if (i > max - 2)
+ return inline_observeArg.arguments[0];
+ return { test : i };
+};
+function inline_observeArg(obj, i) {
+ return argFault_observeArg(i);
+}
+function observeArg(i) {
+ var obj = { test: i };
+ var res = inline_observeArg(obj, i);
+ assertEq(res.test, i);
+ assertRecoveredOnBailout(obj, true);
+}
+
+// Check case where one successor can have multiple times the same predecessor.
+function complexPhi(i) {
+ var obj = { test: i };
+ switch (i) { // TableSwitch
+ case 0: obj.test = 0; break;
+ case 1: obj.test = 1; break;
+ case 2: obj.test = 2; break;
+ case 3: case 4: case 5: case 6:
+ default: obj.test = i; break;
+ case 7: obj.test = 7; break;
+ case 8: obj.test = 8; break;
+ case 9: obj.test = 9; break;
+ }
+ assertEq(obj.test, i);
+ assertRecoveredOnBailout(obj, true);
+}
+
+// Check case where one successor can have multiple times the same predecessor.
+function withinIf(i) {
+ var x = undefined;
+ if (i % 2 == 0) {
+ let obj = { foo: i };
+ x = obj.foo;
+ assertRecoveredOnBailout(obj, true);
+ obj = undefined;
+ } else {
+ let obj = { bar: i };
+ x = obj.bar;
+ assertRecoveredOnBailout(obj, true);
+ obj = undefined;
+ }
+ assertEq(x, i);
+}
+
+// Check case where one successor can have multiple times the same predecessor.
+function unknownLoad(i) {
+ var obj = { foo: i };
+ // Unknown properties are inlined as undefined.
+ assertEq(obj.bar, undefined);
+ assertRecoveredOnBailout(obj, true);
+}
+
+// Check with dynamic slots.
+//
+// This test assumes that creation of an object with 50 slots is optimized;
+// see MaxDynamicSlotsToOptimize.
+function dynamicSlots(i) {
+ var obj = {
+ p0: i + 0, p1: i + 1, p2: i + 2, p3: i + 3, p4: i + 4, p5: i + 5, p6: i + 6, p7: i + 7, p8: i + 8, p9: i + 9, p10: i + 10,
+ p11: i + 11, p12: i + 12, p13: i + 13, p14: i + 14, p15: i + 15, p16: i + 16, p17: i + 17, p18: i + 18, p19: i + 19, p20: i + 20,
+ p21: i + 21, p22: i + 22, p23: i + 23, p24: i + 24, p25: i + 25, p26: i + 26, p27: i + 27, p28: i + 28, p29: i + 29, p30: i + 30,
+ p31: i + 31, p32: i + 32, p33: i + 33, p34: i + 34, p35: i + 35, p36: i + 36, p37: i + 37, p38: i + 38, p39: i + 39, p40: i + 40,
+ p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50
+ };
+ // Add a function call to capture a resumepoint at the end of the call or
+ // inside the inlined block, such as the bailout does not rewind to the
+ // beginning of the function.
+ resumeHere(); bailout();
+ assertEq(obj.p0 + obj.p10 + obj.p20 + obj.p30 + obj.p40, 5 * i + 100);
+ assertRecoveredOnBailout(obj, true);
+}
+
+// Check that we can correctly recover allocations of new objects.
+function Point(x, y)
+{
+ this.x = x;
+ this.y = y;
+}
+
+function createThisWithTemplate(i)
+{
+ var p = new Point(i - 1, i + 1);
+ bailout();
+ assertEq(p.y - p.x, 2);
+ assertRecoveredOnBailout(p, true);
+}
+
+function testNewObject1(i) {
+ var o = { a: 1 };
+ assertRecoveredOnBailout(o, true);
+ return o.a;
+}
+
+var objIdx = 0;
+var uceFault_notSoEmpty3 = eval(`(${uceFault})`.replace('uceFault', 'uceFault_notSoEmpty3'));
+function testNewObjectWithBranchPruning(i) {
+ let obj = {};
+ let idx = objIdx++;
+ if (uceFault_notSoEmpty3(i) || uceFault_notSoEmpty3(i)) {
+ // Branch content removed because never taken. Thus, no uses of obj,
+ // which can then be marked as recovered-on-bailout if foo is ever
+ // called with false.
+ obj.idx = idx;
+ obj.a = 1;
+ obj.b = 2;
+ return obj;
+ }
+ assertRecoveredOnBailout(obj, true);
+ return idx;
+}
+
+for (var i = 0; i < max; i++) {
+ notSoEmpty1(i);
+ notSoEmpty2(i);
+ observeArg(i);
+ complexPhi(i);
+ withinIf(i);
+ dynamicSlots(i);
+ testNewObject1(i);
+ testNewObjectWithBranchPruning(i);
+ unknownLoad(i);
+
+ // TODO: support constructors in scalar replacement (bug 1700422)
+ // createThisWithTemplate(i);
+}
+
+let o = testNewObjectWithBranchPruning(-1);
+assertEq(o.a, 1);
+assertEq(o.b, 2);