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 // support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8));
     39 support_smi_only_arrays = true;
     40 
     41 if (support_smi_only_arrays) {
     42   print("Tests include smi-only arrays.");
     43 } else {
     44   print("Tests do NOT include smi-only arrays.");
     45 }
     46 
     47 var elements_kind = {
     48   fast_smi_only            :  'fast smi only elements',
     49   fast                     :  'fast elements',
     50   fast_double              :  'fast double elements',
     51   dictionary               :  'dictionary elements',
     52   external_byte            :  'external byte elements',
     53   external_unsigned_byte   :  'external unsigned byte elements',
     54   external_short           :  'external short elements',
     55   external_unsigned_short  :  'external unsigned short elements',
     56   external_int             :  'external int elements',
     57   external_unsigned_int    :  'external unsigned int elements',
     58   external_float           :  'external float elements',
     59   external_double          :  'external double elements',
     60   external_pixel           :  'external pixel elements'
     61 }
     62 
     63 function getKind(obj) {
     64   if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only;
     65   if (%HasFastObjectElements(obj)) return elements_kind.fast;
     66   if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
     67   if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
     68 }
     69 
     70 function isHoley(obj) {
     71   if (%HasFastHoleyElements(obj)) return true;
     72   return false;
     73 }
     74 
     75 function assertKind(expected, obj, name_opt) {
     76   if (!support_smi_only_arrays &&
     77       expected == elements_kind.fast_smi_only) {
     78     expected = elements_kind.fast;
     79   }
     80   assertEquals(expected, getKind(obj), name_opt);
     81 }
     82 
     83 function assertHoley(obj, name_opt) {
     84   assertEquals(true, isHoley(obj), name_opt);
     85 }
     86 
     87 function assertNotHoley(obj, name_opt) {
     88   assertEquals(false, isHoley(obj), name_opt);
     89 }
     90 
     91 if (support_smi_only_arrays) {
     92   obj = [];
     93   assertNotHoley(obj);
     94   assertKind(elements_kind.fast_smi_only, obj);
     95 
     96   obj = [1, 2, 3];
     97   assertNotHoley(obj);
     98   assertKind(elements_kind.fast_smi_only, obj);
     99 
    100   obj = new Array();
    101   assertNotHoley(obj);
    102   assertKind(elements_kind.fast_smi_only, obj);
    103 
    104   obj = new Array(0);
    105   assertNotHoley(obj);
    106   assertKind(elements_kind.fast_smi_only, obj);
    107 
    108   obj = new Array(2);
    109   assertHoley(obj);
    110   assertKind(elements_kind.fast_smi_only, obj);
    111 
    112   obj = new Array(1,2,3);
    113   assertNotHoley(obj);
    114   assertKind(elements_kind.fast_smi_only, obj);
    115 
    116   obj = new Array(1, "hi", 2, undefined);
    117   assertNotHoley(obj);
    118   assertKind(elements_kind.fast, obj);
    119 
    120   function fastliteralcase(literal, value) {
    121     literal[0] = value;
    122     return literal;
    123   }
    124 
    125   function get_standard_literal() {
    126     var literal = [1, 2, 3];
    127     return literal;
    128   }
    129 
    130   // Case: [1,2,3] as allocation site
    131   obj = fastliteralcase(get_standard_literal(), 1);
    132   assertKind(elements_kind.fast_smi_only, obj);
    133   obj = fastliteralcase(get_standard_literal(), 1.5);
    134   assertKind(elements_kind.fast_double, obj);
    135   obj = fastliteralcase(get_standard_literal(), 2);
    136   assertKind(elements_kind.fast_double, obj);
    137 
    138   // The test below is in a loop because arrays that live
    139   // at global scope without the chance of being recreated
    140   // don't have allocation site information attached.
    141   for (i = 0; i < 2; i++) {
    142     obj = fastliteralcase([5, 3, 2], 1.5);
    143     assertKind(elements_kind.fast_double, obj);
    144     obj = fastliteralcase([3, 6, 2], 1.5);
    145     assertKind(elements_kind.fast_double, obj);
    146     obj = fastliteralcase([2, 6, 3], 2);
    147     assertKind(elements_kind.fast_smi_only, obj);
    148   }
    149 
    150   // Verify that we will not pretransition the double->fast path.
    151   obj = fastliteralcase(get_standard_literal(), "elliot");
    152   assertKind(elements_kind.fast, obj);
    153   // This fails until we turn off optimistic transitions to the
    154   // most general elements kind seen on keyed stores. It's a goal
    155   // to turn it off, but for now we need it.
    156   // obj = fastliteralcase(3);
    157   // assertKind(elements_kind.fast_double, obj);
    158 
    159   // Make sure this works in crankshafted code too.
    160   %OptimizeFunctionOnNextCall(get_standard_literal);
    161   get_standard_literal();
    162   obj = get_standard_literal();
    163   assertKind(elements_kind.fast_double, obj);
    164 
    165   function fastliteralcase_smifast(value) {
    166     var literal = [1, 2, 3, 4];
    167     literal[0] = value;
    168     return literal;
    169   }
    170 
    171   obj = fastliteralcase_smifast(1);
    172   assertKind(elements_kind.fast_smi_only, obj);
    173   obj = fastliteralcase_smifast("carter");
    174   assertKind(elements_kind.fast, obj);
    175   obj = fastliteralcase_smifast(2);
    176   assertKind(elements_kind.fast, obj);
    177 
    178   // Case: make sure transitions from packed to holey are tracked
    179   function fastliteralcase_smiholey(index, value) {
    180     var literal = [1, 2, 3, 4];
    181     literal[index] = value;
    182     return literal;
    183   }
    184 
    185   obj = fastliteralcase_smiholey(5, 1);
    186   assertKind(elements_kind.fast_smi_only, obj);
    187   assertHoley(obj);
    188   obj = fastliteralcase_smiholey(0, 1);
    189   assertKind(elements_kind.fast_smi_only, obj);
    190   assertHoley(obj);
    191 
    192   function newarraycase_smidouble(value) {
    193     var a = new Array();
    194     a[0] = value;
    195     return a;
    196   }
    197 
    198   // Case: new Array() as allocation site, smi->double
    199   obj = newarraycase_smidouble(1);
    200   assertKind(elements_kind.fast_smi_only, obj);
    201   obj = newarraycase_smidouble(1.5);
    202   assertKind(elements_kind.fast_double, obj);
    203   obj = newarraycase_smidouble(2);
    204   assertKind(elements_kind.fast_double, obj);
    205 
    206   function newarraycase_smiobj(value) {
    207     var a = new Array();
    208     a[0] = value;
    209     return a;
    210   }
    211 
    212   // Case: new Array() as allocation site, smi->fast
    213   obj = newarraycase_smiobj(1);
    214   assertKind(elements_kind.fast_smi_only, obj);
    215   obj = newarraycase_smiobj("gloria");
    216   assertKind(elements_kind.fast, obj);
    217   obj = newarraycase_smiobj(2);
    218   assertKind(elements_kind.fast, obj);
    219 
    220   function newarraycase_length_smidouble(value) {
    221     var a = new Array(3);
    222     a[0] = value;
    223     return a;
    224   }
    225 
    226   // Case: new Array(length) as allocation site
    227   obj = newarraycase_length_smidouble(1);
    228   assertKind(elements_kind.fast_smi_only, obj);
    229   obj = newarraycase_length_smidouble(1.5);
    230   assertKind(elements_kind.fast_double, obj);
    231   obj = newarraycase_length_smidouble(2);
    232   assertKind(elements_kind.fast_double, obj);
    233 
    234   // Try to continue the transition to fast object, but
    235   // we will not pretransition from double->fast, because
    236   // it may hurt performance ("poisoning").
    237   obj = newarraycase_length_smidouble("coates");
    238   assertKind(elements_kind.fast, obj);
    239   obj = newarraycase_length_smidouble(2.5);
    240   // However, because of optimistic transitions, we will
    241   // transition to the most general kind of elements kind found,
    242   // therefore I can't count on this assert yet.
    243   // assertKind(elements_kind.fast_double, obj);
    244 
    245   function newarraycase_length_smiobj(value) {
    246     var a = new Array(3);
    247     a[0] = value;
    248     return a;
    249   }
    250 
    251   // Case: new Array(<length>) as allocation site, smi->fast
    252   obj = newarraycase_length_smiobj(1);
    253   assertKind(elements_kind.fast_smi_only, obj);
    254   obj = newarraycase_length_smiobj("gloria");
    255   assertKind(elements_kind.fast, obj);
    256   obj = newarraycase_length_smiobj(2);
    257   assertKind(elements_kind.fast, obj);
    258 
    259   function newarraycase_list_smidouble(value) {
    260     var a = new Array(1, 2, 3);
    261     a[0] = value;
    262     return a;
    263   }
    264 
    265   obj = newarraycase_list_smidouble(1);
    266   assertKind(elements_kind.fast_smi_only, obj);
    267   obj = newarraycase_list_smidouble(1.5);
    268   assertKind(elements_kind.fast_double, obj);
    269   obj = newarraycase_list_smidouble(2);
    270   assertKind(elements_kind.fast_double, obj);
    271 
    272   function newarraycase_list_smiobj(value) {
    273     var a = new Array(4, 5, 6);
    274     a[0] = value;
    275     return a;
    276   }
    277 
    278   obj = newarraycase_list_smiobj(1);
    279   assertKind(elements_kind.fast_smi_only, obj);
    280   obj = newarraycase_list_smiobj("coates");
    281   assertKind(elements_kind.fast, obj);
    282   obj = newarraycase_list_smiobj(2);
    283   assertKind(elements_kind.fast, obj);
    284 
    285   // Case: array constructor calls with out of date feedback.
    286   // The boilerplate should incorporate all feedback, but the input array
    287   // should be minimally transitioned based on immediate need.
    288   (function() {
    289     function foo(i) {
    290       // We have two cases, one for literals one for constructed arrays.
    291       var a = (i == 0)
    292         ? [1, 2, 3]
    293         : new Array(1, 2, 3);
    294       return a;
    295     }
    296 
    297     for (i = 0; i < 2; i++) {
    298       a = foo(i);
    299       b = foo(i);
    300       b[5] = 1;  // boilerplate goes holey
    301       assertHoley(foo(i));
    302       a[0] = 3.5;  // boilerplate goes holey double
    303       assertKind(elements_kind.fast_double, a);
    304       assertNotHoley(a);
    305       c = foo(i);
    306       assertKind(elements_kind.fast_double, c);
    307       assertHoley(c);
    308     }
    309   })();
    310 
    311   function newarraycase_onearg(len, value) {
    312     var a = new Array(len);
    313     a[0] = value;
    314     return a;
    315   }
    316 
    317   obj = newarraycase_onearg(5, 3.5);
    318   assertKind(elements_kind.fast_double, obj);
    319   obj = newarraycase_onearg(10, 5);
    320   assertKind(elements_kind.fast_double, obj);
    321   obj = newarraycase_onearg(0, 5);
    322   assertKind(elements_kind.fast_double, obj);
    323   // Now pass a length that forces the dictionary path.
    324   obj = newarraycase_onearg(100000, 5);
    325   assertKind(elements_kind.dictionary, obj);
    326   assertTrue(obj.length == 100000);
    327 
    328   // Verify that cross context calls work
    329   var realmA = Realm.current();
    330   var realmB = Realm.create();
    331   assertEquals(0, realmA);
    332   assertEquals(1, realmB);
    333 
    334   function instanceof_check(type) {
    335     assertTrue(new type() instanceof type);
    336     assertTrue(new type(5) instanceof type);
    337     assertTrue(new type(1,2,3) instanceof type);
    338   }
    339 
    340   function instanceof_check2(type) {
    341     assertTrue(new type() instanceof type);
    342     assertTrue(new type(5) instanceof type);
    343     assertTrue(new type(1,2,3) instanceof type);
    344   }
    345 
    346   var realmBArray = Realm.eval(realmB, "Array");
    347   instanceof_check(Array);
    348   instanceof_check(realmBArray);
    349 
    350   // instanceof_check2 is here because the call site goes through a state.
    351   // Since instanceof_check(Array) was first called with the current context
    352   // Array function, it went from (uninit->Array) then (Array->megamorphic).
    353   // We'll get a different state traversal if we start with realmBArray.
    354   // It'll go (uninit->realmBArray) then (realmBArray->megamorphic). Recognize
    355   // that state "Array" implies an AllocationSite is present, and code is
    356   // configured to use it.
    357   instanceof_check2(realmBArray);
    358   instanceof_check2(Array);
    359 
    360   %OptimizeFunctionOnNextCall(instanceof_check);
    361 
    362   // No de-opt will occur because HCallNewArray wasn't selected, on account of
    363   // the call site not being monomorphic to Array.
    364   instanceof_check(Array);
    365   assertOptimized(instanceof_check);
    366   instanceof_check(realmBArray);
    367   assertOptimized(instanceof_check);
    368 
    369   // Try to optimize again, but first clear all type feedback, and allow it
    370   // to be monomorphic on first call. Only after crankshafting do we introduce
    371   // realmBArray. This should deopt the method.
    372   %DeoptimizeFunction(instanceof_check);
    373   %ClearFunctionTypeFeedback(instanceof_check);
    374   instanceof_check(Array);
    375   instanceof_check(Array);
    376   %OptimizeFunctionOnNextCall(instanceof_check);
    377   instanceof_check(Array);
    378   assertOptimized(instanceof_check);
    379 
    380   instanceof_check(realmBArray);
    381   assertUnoptimized(instanceof_check);
    382 }
    383