recover-objects.js (7846B)
1 // |jit-test| --ion-pruning=on; --fast-warmup; --baseline-offthread-compile=off 2 3 var max = 200; 4 5 // Ion eager fails the test below because we have not yet created any 6 // template object in baseline before running the content of the top-level 7 // function. 8 if (getJitCompilerOptions()["ion.warmup.trigger"] <= max - 10) 9 setJitCompilerOption("ion.warmup.trigger", max - 10); 10 11 // Force Inlining heuristics to always inline the functions which have the same 12 // number of use count. 13 setJitCompilerOption("ion.warmup.trigger", getJitCompilerOptions()["ion.warmup.trigger"]); 14 15 // This test checks that we are able to remove the getprop & setprop with scalar 16 // replacement, so we should not force inline caches, as this would skip the 17 // generation of getprop & setprop instructions. 18 if (getJitCompilerOptions()["ion.forceinlineCaches"]) 19 setJitCompilerOption("ion.forceinlineCaches", 0); 20 21 // Prevent the GC from cancelling Ion compilations, when we expect them to succeed 22 gczeal(0); 23 24 function resumeHere() {} 25 var uceFault = function (i) { 26 if (i > max - 2) 27 uceFault = function (i) { return true; }; 28 return false; 29 }; 30 31 32 // Without "use script" in the inner function, the arguments might be 33 // observable. 34 function inline_notSoEmpty1(a, b, c, d) { 35 // This function is not strict, so we might be able to observe its 36 // arguments, if somebody ever called fun.arguments inside it. 37 return { v: (a.v + b.v + c.v + d.v - 10) / 4 }; 38 } 39 var uceFault_notSoEmpty1 = eval(`(${uceFault})`.replace('uceFault', 'uceFault_notSoEmpty1')); 40 function notSoEmpty1() { 41 var a = { v: i }; 42 var b = { v: 1 + a.v }; 43 var c = { v: 2 + b.v }; 44 var d = { v: 3 + c.v }; 45 var unused = { v: 4 + d.v }; 46 var res = inline_notSoEmpty1(a, b, c, d); 47 if (uceFault_notSoEmpty1(i) || uceFault_notSoEmpty1(i)) 48 assertEq(i, res.v); 49 // Note, that even if the arguments are observable, we are capable of 50 // optimizing these cases by executing recover instruction before the 51 // execution of the bailout. This ensures that the observed objects are 52 // allocated once and used by the unexpected observation and the bailout. 53 assertRecoveredOnBailout(a, true); 54 assertRecoveredOnBailout(b, true); 55 assertRecoveredOnBailout(c, true); 56 assertRecoveredOnBailout(d, true); 57 assertRecoveredOnBailout(unused, true); 58 // This can only be recovered on bailout iff either we have type 59 // information for the property access in the branch, or the branch is 60 // removed before scalar replacement. 61 assertRecoveredOnBailout(res, true); 62 } 63 64 // Check that we can recover objects with their content. 65 function inline_notSoEmpty2(a, b, c, d) { 66 "use strict"; 67 return { v: (a.v + b.v + c.v + d.v - 10) / 4 }; 68 } 69 var uceFault_notSoEmpty2 = eval(`(${uceFault})`.replace('uceFault', 'uceFault_notSoEmpty2')); 70 function notSoEmpty2(i) { 71 var a = { v: i }; 72 var b = { v: 1 + a.v }; 73 var c = { v: 2 + b.v }; 74 var d = { v: 3 + c.v }; 75 var unused = { v: 4 + d.v }; 76 var res = inline_notSoEmpty2(a, b, c, d); 77 if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i)) 78 assertEq(i, res.v); 79 assertRecoveredOnBailout(a, true); 80 assertRecoveredOnBailout(b, true); 81 assertRecoveredOnBailout(c, true); 82 assertRecoveredOnBailout(d, true); 83 assertRecoveredOnBailout(unused, true); 84 // This can only be recovered on bailout iff either we have type 85 // information for the property access in the branch, or the branch is 86 // removed before scalar replacement. 87 assertRecoveredOnBailout(res, true); 88 } 89 90 // Check that we can recover objects with their content. 91 var argFault_observeArg = function (i) { 92 if (i > max - 2) 93 return inline_observeArg.arguments[0]; 94 return { test : i }; 95 }; 96 function inline_observeArg(obj, i) { 97 return argFault_observeArg(i); 98 } 99 function observeArg(i) { 100 var obj = { test: i }; 101 var res = inline_observeArg(obj, i); 102 assertEq(res.test, i); 103 assertRecoveredOnBailout(obj, true); 104 } 105 106 // Check case where one successor can have multiple times the same predecessor. 107 function complexPhi(i) { 108 var obj = { test: i }; 109 switch (i) { // TableSwitch 110 case 0: obj.test = 0; break; 111 case 1: obj.test = 1; break; 112 case 2: obj.test = 2; break; 113 case 3: case 4: case 5: case 6: 114 default: obj.test = i; break; 115 case 7: obj.test = 7; break; 116 case 8: obj.test = 8; break; 117 case 9: obj.test = 9; break; 118 } 119 assertEq(obj.test, i); 120 assertRecoveredOnBailout(obj, true); 121 } 122 123 // Check case where one successor can have multiple times the same predecessor. 124 function withinIf(i) { 125 var x = undefined; 126 if (i % 2 == 0) { 127 let obj = { foo: i }; 128 x = obj.foo; 129 assertRecoveredOnBailout(obj, true); 130 obj = undefined; 131 } else { 132 let obj = { bar: i }; 133 x = obj.bar; 134 assertRecoveredOnBailout(obj, true); 135 obj = undefined; 136 } 137 assertEq(x, i); 138 } 139 140 // Check case where one successor can have multiple times the same predecessor. 141 function unknownLoad(i) { 142 var obj = { foo: i }; 143 // Unknown properties are inlined as undefined. 144 assertEq(obj.bar, undefined); 145 assertRecoveredOnBailout(obj, true); 146 } 147 148 // Check with dynamic slots. 149 // 150 // This test assumes that creation of an object with 50 slots is optimized; 151 // see MaxDynamicSlotsToOptimize. 152 function dynamicSlots(i) { 153 var obj = { 154 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, 155 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, 156 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, 157 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, 158 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 159 }; 160 // Add a function call to capture a resumepoint at the end of the call or 161 // inside the inlined block, such as the bailout does not rewind to the 162 // beginning of the function. 163 resumeHere(); bailout(); 164 assertEq(obj.p0 + obj.p10 + obj.p20 + obj.p30 + obj.p40, 5 * i + 100); 165 assertRecoveredOnBailout(obj, true); 166 } 167 168 // Check that we can correctly recover allocations of new objects. 169 function Point(x, y) 170 { 171 this.x = x; 172 this.y = y; 173 } 174 175 function createThisWithTemplate(i) 176 { 177 var p = new Point(i - 1, i + 1); 178 bailout(); 179 assertEq(p.y - p.x, 2); 180 assertRecoveredOnBailout(p, true); 181 } 182 183 function testNewObject1(i) { 184 var o = { a: 1 }; 185 assertRecoveredOnBailout(o, true); 186 return o.a; 187 } 188 189 var objIdx = 0; 190 var uceFault_notSoEmpty3 = eval(`(${uceFault})`.replace('uceFault', 'uceFault_notSoEmpty3')); 191 function testNewObjectWithBranchPruning(i) { 192 let obj = {}; 193 let idx = objIdx++; 194 if (uceFault_notSoEmpty3(i) || uceFault_notSoEmpty3(i)) { 195 // Branch content removed because never taken. Thus, no uses of obj, 196 // which can then be marked as recovered-on-bailout if foo is ever 197 // called with false. 198 obj.idx = idx; 199 obj.a = 1; 200 obj.b = 2; 201 return obj; 202 } 203 assertRecoveredOnBailout(obj, true); 204 return idx; 205 } 206 207 for (var i = 0; i < max; i++) { 208 notSoEmpty1(i); 209 notSoEmpty2(i); 210 observeArg(i); 211 complexPhi(i); 212 withinIf(i); 213 dynamicSlots(i); 214 testNewObject1(i); 215 testNewObjectWithBranchPruning(i); 216 unknownLoad(i); 217 218 // TODO: support constructors in scalar replacement (bug 1700422) 219 // createThisWithTemplate(i); 220 } 221 222 let o = testNewObjectWithBranchPruning(-1); 223 assertEq(o.a, 1); 224 assertEq(o.b, 2);