tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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);