Home | History | Annotate | Download | only in mjsunit
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 // Flags: --allow-natives-syntax --smi-only-arrays --expose-gc
     29 // Flags: --track-allocation-sites --noalways-opt
     30 
     31 // Test element kind of objects.
     32 // Since --smi-only-arrays affects builtins, its default setting at compile
     33 // time sticks if built with snapshot.  If --smi-only-arrays is deactivated
     34 // by default, only a no-snapshot build actually has smi-only arrays enabled
     35 // in this test case.  Depending on whether smi-only arrays are actually
     36 // enabled, this test takes the appropriate code path to check smi-only arrays.
     37 
     38 // Reset the GC stress mode to be off. Needed because AllocationMementos only
     39 // live for one gc, so a gc that happens in certain fragile areas of the test
     40 // can break assumptions.
     41 %SetFlags("--gc-interval=-1")
     42 
     43 // support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8));
     44 support_smi_only_arrays = true;
     45 
     46 if (support_smi_only_arrays) {
     47   print("Tests include smi-only arrays.");
     48 } else {
     49   print("Tests do NOT include smi-only arrays.");
     50 }
     51 
     52 var elements_kind = {
     53   fast_smi_only            :  'fast smi only elements',
     54   fast                     :  'fast elements',
     55   fast_double              :  'fast double elements',
     56   dictionary               :  'dictionary elements',
     57   external_byte            :  'external byte elements',
     58   external_unsigned_byte   :  'external unsigned byte elements',
     59   external_short           :  'external short elements',
     60   external_unsigned_short  :  'external unsigned short elements',
     61   external_int             :  'external int elements',
     62   external_unsigned_int    :  'external unsigned int elements',
     63   external_float           :  'external float elements',
     64   external_double          :  'external double elements',
     65   external_pixel           :  'external pixel elements'
     66 }
     67 
     68 function getKind(obj) {
     69   if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only;
     70   if (%HasFastObjectElements(obj)) return elements_kind.fast;
     71   if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
     72   if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
     73 }
     74 
     75 function isHoley(obj) {
     76   if (%HasFastHoleyElements(obj)) return true;
     77   return false;
     78 }
     79 
     80 function assertKind(expected, obj, name_opt) {
     81   if (!support_smi_only_arrays &&
     82       expected == elements_kind.fast_smi_only) {
     83     expected = elements_kind.fast;
     84   }
     85   assertEquals(expected, getKind(obj), name_opt);
     86 }
     87 
     88 function assertHoley(obj, name_opt) {
     89   assertEquals(true, isHoley(obj), name_opt);
     90 }
     91 
     92 function assertNotHoley(obj, name_opt) {
     93   assertEquals(false, isHoley(obj), name_opt);
     94 }
     95 
     96 if (support_smi_only_arrays) {
     97   obj = [];
     98   assertNotHoley(obj);
     99   assertKind(elements_kind.fast_smi_only, obj);
    100 
    101   obj = [1, 2, 3];
    102   assertNotHoley(obj);
    103   assertKind(elements_kind.fast_smi_only, obj);
    104 
    105   obj = new Array();
    106   assertNotHoley(obj);
    107   assertKind(elements_kind.fast_smi_only, obj);
    108 
    109   obj = new Array(0);
    110   assertNotHoley(obj);
    111   assertKind(elements_kind.fast_smi_only, obj);
    112 
    113   obj = new Array(2);
    114   assertHoley(obj);
    115   assertKind(elements_kind.fast_smi_only, obj);
    116 
    117   obj = new Array(1,2,3);
    118   assertNotHoley(obj);
    119   assertKind(elements_kind.fast_smi_only, obj);
    120 
    121   obj = new Array(1, "hi", 2, undefined);
    122   assertNotHoley(obj);
    123   assertKind(elements_kind.fast, obj);
    124 
    125   function fastliteralcase(literal, value) {
    126     literal[0] = value;
    127     return literal;
    128   }
    129 
    130   function get_standard_literal() {
    131     var literal = [1, 2, 3];
    132     return literal;
    133   }
    134 
    135   // Case: [1,2,3] as allocation site
    136   obj = fastliteralcase(get_standard_literal(), 1);
    137   assertKind(elements_kind.fast_smi_only, obj);
    138   obj = fastliteralcase(get_standard_literal(), 1.5);
    139   assertKind(elements_kind.fast_double, obj);
    140   obj = fastliteralcase(get_standard_literal(), 2);
    141   assertKind(elements_kind.fast_double, obj);
    142 
    143   // The test below is in a loop because arrays that live
    144   // at global scope without the chance of being recreated
    145   // don't have allocation site information attached.
    146   for (i = 0; i < 2; i++) {
    147     obj = fastliteralcase([5, 3, 2], 1.5);
    148     assertKind(elements_kind.fast_double, obj);
    149     obj = fastliteralcase([3, 6, 2], 1.5);
    150     assertKind(elements_kind.fast_double, obj);
    151 
    152     // Note: thanks to pessimistic transition store stubs, we'll attempt
    153     // to transition to the most general elements kind seen at a particular
    154     // store site. So, the elements kind will be double.
    155     obj = fastliteralcase([2, 6, 3], 2);
    156     assertKind(elements_kind.fast_double, obj);
    157   }
    158 
    159   // Verify that we will not pretransition the double->fast path.
    160   obj = fastliteralcase(get_standard_literal(), "elliot");
    161   assertKind(elements_kind.fast, obj);
    162   obj = fastliteralcase(get_standard_literal(), 3);
    163   assertKind(elements_kind.fast, obj);
    164 
    165   // Make sure this works in crankshafted code too.
    166   %OptimizeFunctionOnNextCall(get_standard_literal);
    167   get_standard_literal();
    168   obj = get_standard_literal();
    169   assertKind(elements_kind.fast, obj);
    170 
    171   function fastliteralcase_smifast(value) {
    172     var literal = [1, 2, 3, 4];
    173     literal[0] = value;
    174     return literal;
    175   }
    176 
    177   obj = fastliteralcase_smifast(1);
    178   assertKind(elements_kind.fast_smi_only, obj);
    179   obj = fastliteralcase_smifast("carter");
    180   assertKind(elements_kind.fast, obj);
    181   obj = fastliteralcase_smifast(2);
    182   assertKind(elements_kind.fast, obj);
    183 
    184   // Case: make sure transitions from packed to holey are tracked
    185   function fastliteralcase_smiholey(index, value) {
    186     var literal = [1, 2, 3, 4];
    187     literal[index] = value;
    188     return literal;
    189   }
    190 
    191   obj = fastliteralcase_smiholey(5, 1);
    192   assertKind(elements_kind.fast_smi_only, obj);
    193   assertHoley(obj);
    194   obj = fastliteralcase_smiholey(0, 1);
    195   assertKind(elements_kind.fast_smi_only, obj);
    196   assertHoley(obj);
    197 
    198   function newarraycase_smidouble(value) {
    199     var a = new Array();
    200     a[0] = value;
    201     return a;
    202   }
    203 
    204   // Case: new Array() as allocation site, smi->double
    205   obj = newarraycase_smidouble(1);
    206   assertKind(elements_kind.fast_smi_only, obj);
    207   obj = newarraycase_smidouble(1.5);
    208   assertKind(elements_kind.fast_double, obj);
    209   obj = newarraycase_smidouble(2);
    210   assertKind(elements_kind.fast_double, obj);
    211 
    212   function newarraycase_smiobj(value) {
    213     var a = new Array();
    214     a[0] = value;
    215     return a;
    216   }
    217 
    218   // Case: new Array() as allocation site, smi->fast
    219   obj = newarraycase_smiobj(1);
    220   assertKind(elements_kind.fast_smi_only, obj);
    221   obj = newarraycase_smiobj("gloria");
    222   assertKind(elements_kind.fast, obj);
    223   obj = newarraycase_smiobj(2);
    224   assertKind(elements_kind.fast, obj);
    225 
    226   function newarraycase_length_smidouble(value) {
    227     var a = new Array(3);
    228     a[0] = value;
    229     return a;
    230   }
    231 
    232   // Case: new Array(length) as allocation site
    233   obj = newarraycase_length_smidouble(1);
    234   assertKind(elements_kind.fast_smi_only, obj);
    235   obj = newarraycase_length_smidouble(1.5);
    236   assertKind(elements_kind.fast_double, obj);
    237   obj = newarraycase_length_smidouble(2);
    238   assertKind(elements_kind.fast_double, obj);
    239 
    240   // Try to continue the transition to fast object. This won't work for
    241   // constructed arrays because constructor dispatch is done on the
    242   // elements kind, and a DOUBLE array constructor won't create an allocation
    243   // memento.
    244   obj = newarraycase_length_smidouble("coates");
    245   assertKind(elements_kind.fast, obj);
    246   obj = newarraycase_length_smidouble(2);
    247   assertKind(elements_kind.fast_double, obj);
    248 
    249   function newarraycase_length_smiobj(value) {
    250     var a = new Array(3);
    251     a[0] = value;
    252     return a;
    253   }
    254 
    255   // Case: new Array(<length>) as allocation site, smi->fast
    256   obj = newarraycase_length_smiobj(1);
    257   assertKind(elements_kind.fast_smi_only, obj);
    258   obj = newarraycase_length_smiobj("gloria");
    259   assertKind(elements_kind.fast, obj);
    260   obj = newarraycase_length_smiobj(2);
    261   assertKind(elements_kind.fast, obj);
    262 
    263   function newarraycase_list_smidouble(value) {
    264     var a = new Array(1, 2, 3);
    265     a[0] = value;
    266     return a;
    267   }
    268 
    269   obj = newarraycase_list_smidouble(1);
    270   assertKind(elements_kind.fast_smi_only, obj);
    271   obj = newarraycase_list_smidouble(1.5);
    272   assertKind(elements_kind.fast_double, obj);
    273   obj = newarraycase_list_smidouble(2);
    274   assertKind(elements_kind.fast_double, obj);
    275 
    276   function newarraycase_list_smiobj(value) {
    277     var a = new Array(4, 5, 6);
    278     a[0] = value;
    279     return a;
    280   }
    281 
    282   obj = newarraycase_list_smiobj(1);
    283   assertKind(elements_kind.fast_smi_only, obj);
    284   obj = newarraycase_list_smiobj("coates");
    285   assertKind(elements_kind.fast, obj);
    286   obj = newarraycase_list_smiobj(2);
    287   assertKind(elements_kind.fast, obj);
    288 
    289   // Case: array constructor calls with out of date feedback.
    290   // The boilerplate should incorporate all feedback, but the input array
    291   // should be minimally transitioned based on immediate need.
    292   (function() {
    293     function foo(i) {
    294       // We have two cases, one for literals one for constructed arrays.
    295       var a = (i == 0)
    296         ? [1, 2, 3]
    297         : new Array(1, 2, 3);
    298       return a;
    299     }
    300 
    301     for (i = 0; i < 2; i++) {
    302       a = foo(i);
    303       b = foo(i);
    304       b[5] = 1;  // boilerplate goes holey
    305       assertHoley(foo(i));
    306       a[0] = 3.5;  // boilerplate goes holey double
    307       assertKind(elements_kind.fast_double, a);
    308       assertNotHoley(a);
    309       c = foo(i);
    310       assertKind(elements_kind.fast_double, c);
    311       assertHoley(c);
    312     }
    313   })();
    314 
    315   function newarraycase_onearg(len, value) {
    316     var a = new Array(len);
    317     a[0] = value;
    318     return a;
    319   }
    320 
    321   obj = newarraycase_onearg(5, 3.5);
    322   assertKind(elements_kind.fast_double, obj);
    323   obj = newarraycase_onearg(10, 5);
    324   assertKind(elements_kind.fast_double, obj);
    325   obj = newarraycase_onearg(0, 5);
    326   assertKind(elements_kind.fast_double, obj);
    327   // Now pass a length that forces the dictionary path.
    328   obj = newarraycase_onearg(100000, 5);
    329   assertKind(elements_kind.dictionary, obj);
    330   assertTrue(obj.length == 100000);
    331 
    332   // Verify that cross context calls work
    333   var realmA = Realm.current();
    334   var realmB = Realm.create();
    335   assertEquals(0, realmA);
    336   assertEquals(1, realmB);
    337 
    338   function instanceof_check(type) {
    339     assertTrue(new type() instanceof type);
    340     assertTrue(new type(5) instanceof type);
    341     assertTrue(new type(1,2,3) instanceof type);
    342   }
    343 
    344   function instanceof_check2(type) {
    345     assertTrue(new type() instanceof type);
    346     assertTrue(new type(5) instanceof type);
    347     assertTrue(new type(1,2,3) instanceof type);
    348   }
    349 
    350   var realmBArray = Realm.eval(realmB, "Array");
    351   instanceof_check(Array);
    352   instanceof_check(realmBArray);
    353 
    354   // instanceof_check2 is here because the call site goes through a state.
    355   // Since instanceof_check(Array) was first called with the current context
    356   // Array function, it went from (uninit->Array) then (Array->megamorphic).
    357   // We'll get a different state traversal if we start with realmBArray.
    358   // It'll go (uninit->realmBArray) then (realmBArray->megamorphic). Recognize
    359   // that state "Array" implies an AllocationSite is present, and code is
    360   // configured to use it.
    361   instanceof_check2(realmBArray);
    362   instanceof_check2(Array);
    363 
    364   %OptimizeFunctionOnNextCall(instanceof_check);
    365 
    366   // No de-opt will occur because HCallNewArray wasn't selected, on account of
    367   // the call site not being monomorphic to Array.
    368   instanceof_check(Array);
    369   assertOptimized(instanceof_check);
    370   instanceof_check(realmBArray);
    371   assertOptimized(instanceof_check);
    372 
    373   // Try to optimize again, but first clear all type feedback, and allow it
    374   // to be monomorphic on first call. Only after crankshafting do we introduce
    375   // realmBArray. This should deopt the method.
    376   %DeoptimizeFunction(instanceof_check);
    377   %ClearFunctionTypeFeedback(instanceof_check);
    378   instanceof_check(Array);
    379   instanceof_check(Array);
    380   %OptimizeFunctionOnNextCall(instanceof_check);
    381   instanceof_check(Array);
    382   assertOptimized(instanceof_check);
    383 
    384   instanceof_check(realmBArray);
    385   assertUnoptimized(instanceof_check);
    386 
    387   // Case: make sure nested arrays benefit from allocation site feedback as
    388   // well.
    389   (function() {
    390     // Make sure we handle nested arrays
    391    function get_nested_literal() {
    392      var literal = [[1,2,3,4], [2], [3]];
    393      return literal;
    394    }
    395 
    396    obj = get_nested_literal();
    397    assertKind(elements_kind.fast, obj);
    398    obj[0][0] = 3.5;
    399    obj[2][0] = "hello";
    400    obj = get_nested_literal();
    401    assertKind(elements_kind.fast_double, obj[0]);
    402    assertKind(elements_kind.fast_smi_only, obj[1]);
    403    assertKind(elements_kind.fast, obj[2]);
    404 
    405    // A more complex nested literal case.
    406    function get_deep_nested_literal() {
    407      var literal = [[1], [[2], "hello"], 3, [4]];
    408      return literal;
    409    }
    410 
    411    obj = get_deep_nested_literal();
    412    assertKind(elements_kind.fast_smi_only, obj[1][0]);
    413    obj[0][0] = 3.5;
    414    obj[1][0][0] = "goodbye";
    415    assertKind(elements_kind.fast_double, obj[0]);
    416    assertKind(elements_kind.fast, obj[1][0]);
    417 
    418    obj = get_deep_nested_literal();
    419    assertKind(elements_kind.fast_double, obj[0]);
    420    assertKind(elements_kind.fast, obj[1][0]);
    421   })();
    422 
    423 
    424   // Make sure object literals with array fields benefit from the type feedback
    425   // that allocation mementos provide.
    426   (function() {
    427     // A literal in an object
    428     function get_object_literal() {
    429       var literal = {
    430         array: [1,2,3],
    431         data: 3.5
    432       };
    433       return literal;
    434     }
    435 
    436     obj = get_object_literal();
    437     assertKind(elements_kind.fast_smi_only, obj.array);
    438     obj.array[1] = 3.5;
    439     assertKind(elements_kind.fast_double, obj.array);
    440     obj = get_object_literal();
    441     assertKind(elements_kind.fast_double, obj.array);
    442 
    443     function get_nested_object_literal() {
    444       var literal = {
    445         array: [[1],[2],[3]],
    446         data: 3.5
    447       };
    448       return literal;
    449     }
    450 
    451     obj = get_nested_object_literal();
    452     assertKind(elements_kind.fast, obj.array);
    453     assertKind(elements_kind.fast_smi_only, obj.array[1]);
    454     obj.array[1][0] = 3.5;
    455     assertKind(elements_kind.fast_double, obj.array[1]);
    456     obj = get_nested_object_literal();
    457     assertKind(elements_kind.fast_double, obj.array[1]);
    458 
    459     %OptimizeFunctionOnNextCall(get_nested_object_literal);
    460     get_nested_object_literal();
    461     obj = get_nested_object_literal();
    462     assertKind(elements_kind.fast_double, obj.array[1]);
    463 
    464     // Make sure we handle nested arrays
    465     function get_nested_literal() {
    466       var literal = [[1,2,3,4], [2], [3]];
    467       return literal;
    468     }
    469 
    470     obj = get_nested_literal();
    471     assertKind(elements_kind.fast, obj);
    472     obj[0][0] = 3.5;
    473     obj[2][0] = "hello";
    474     obj = get_nested_literal();
    475     assertKind(elements_kind.fast_double, obj[0]);
    476     assertKind(elements_kind.fast_smi_only, obj[1]);
    477     assertKind(elements_kind.fast, obj[2]);
    478 
    479     // A more complex nested literal case.
    480     function get_deep_nested_literal() {
    481       var literal = [[1], [[2], "hello"], 3, [4]];
    482       return literal;
    483     }
    484 
    485     obj = get_deep_nested_literal();
    486     assertKind(elements_kind.fast_smi_only, obj[1][0]);
    487     obj[0][0] = 3.5;
    488     obj[1][0][0] = "goodbye";
    489     assertKind(elements_kind.fast_double, obj[0]);
    490     assertKind(elements_kind.fast, obj[1][0]);
    491 
    492     obj = get_deep_nested_literal();
    493     assertKind(elements_kind.fast_double, obj[0]);
    494     assertKind(elements_kind.fast, obj[1][0]);
    495   })();
    496 }
    497