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 --expose-gc
     29 // Flags: --noalways-opt
     30 
     31 var elements_kind = {
     32   fast_smi_only            :  'fast smi only elements',
     33   fast                     :  'fast elements',
     34   fast_double              :  'fast double elements',
     35   dictionary               :  'dictionary elements',
     36   external_byte            :  'external byte elements',
     37   external_unsigned_byte   :  'external unsigned byte elements',
     38   external_short           :  'external short elements',
     39   external_unsigned_short  :  'external unsigned short elements',
     40   external_int             :  'external int elements',
     41   external_unsigned_int    :  'external unsigned int elements',
     42   external_float           :  'external float elements',
     43   external_double          :  'external double elements',
     44   external_pixel           :  'external pixel elements'
     45 }
     46 
     47 function getKind(obj) {
     48   if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only;
     49   if (%HasFastObjectElements(obj)) return elements_kind.fast;
     50   if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
     51   if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
     52 }
     53 
     54 function isHoley(obj) {
     55   if (%HasFastHoleyElements(obj)) return true;
     56   return false;
     57 }
     58 
     59 function assertKind(expected, obj, name_opt) {
     60   assertEquals(expected, getKind(obj), name_opt);
     61 }
     62 
     63 function assertHoley(obj, name_opt) {
     64   assertEquals(true, isHoley(obj), name_opt);
     65 }
     66 
     67 function assertNotHoley(obj, name_opt) {
     68   assertEquals(false, isHoley(obj), name_opt);
     69 }
     70 
     71 obj = [];
     72 assertNotHoley(obj);
     73 assertKind(elements_kind.fast_smi_only, obj);
     74 
     75 obj = [1, 2, 3];
     76 assertNotHoley(obj);
     77 assertKind(elements_kind.fast_smi_only, obj);
     78 
     79 obj = new Array();
     80 assertNotHoley(obj);
     81 assertKind(elements_kind.fast_smi_only, obj);
     82 
     83 obj = new Array(0);
     84 assertNotHoley(obj);
     85 assertKind(elements_kind.fast_smi_only, obj);
     86 
     87 obj = new Array(2);
     88 assertHoley(obj);
     89 assertKind(elements_kind.fast_smi_only, obj);
     90 
     91 obj = new Array(1,2,3);
     92 assertNotHoley(obj);
     93 assertKind(elements_kind.fast_smi_only, obj);
     94 
     95 obj = new Array(1, "hi", 2, undefined);
     96 assertNotHoley(obj);
     97 assertKind(elements_kind.fast, obj);
     98 
     99 function fastliteralcase(literal, value) {
    100   literal[0] = value;
    101   return literal;
    102 }
    103 
    104 function get_standard_literal() {
    105   var literal = [1, 2, 3];
    106   return literal;
    107 }
    108 
    109 // Case: [1,2,3] as allocation site
    110 obj = fastliteralcase(get_standard_literal(), 1);
    111 assertKind(elements_kind.fast_smi_only, obj);
    112 obj = fastliteralcase(get_standard_literal(), 1.5);
    113 assertKind(elements_kind.fast_double, obj);
    114 obj = fastliteralcase(get_standard_literal(), 2);
    115 assertKind(elements_kind.fast_double, obj);
    116 
    117 // The test below is in a loop because arrays that live
    118 // at global scope without the chance of being recreated
    119 // don't have allocation site information attached.
    120 for (i = 0; i < 2; i++) {
    121   obj = fastliteralcase([5, 3, 2], 1.5);
    122   assertKind(elements_kind.fast_double, obj);
    123   obj = fastliteralcase([3, 6, 2], 1.5);
    124   assertKind(elements_kind.fast_double, obj);
    125 
    126   // Note: thanks to pessimistic transition store stubs, we'll attempt
    127   // to transition to the most general elements kind seen at a particular
    128   // store site. So, the elements kind will be double.
    129   obj = fastliteralcase([2, 6, 3], 2);
    130   assertKind(elements_kind.fast_double, obj);
    131 }
    132 
    133 // Verify that we will not pretransition the double->fast path.
    134 obj = fastliteralcase(get_standard_literal(), "elliot");
    135 assertKind(elements_kind.fast, obj);
    136 obj = fastliteralcase(get_standard_literal(), 3);
    137 assertKind(elements_kind.fast, obj);
    138 
    139 // Make sure this works in crankshafted code too.
    140   %OptimizeFunctionOnNextCall(get_standard_literal);
    141 get_standard_literal();
    142 obj = get_standard_literal();
    143 assertKind(elements_kind.fast, obj);
    144 
    145 function fastliteralcase_smifast(value) {
    146   var literal = [1, 2, 3, 4];
    147   literal[0] = value;
    148   return literal;
    149 }
    150 
    151 obj = fastliteralcase_smifast(1);
    152 assertKind(elements_kind.fast_smi_only, obj);
    153 obj = fastliteralcase_smifast("carter");
    154 assertKind(elements_kind.fast, obj);
    155 obj = fastliteralcase_smifast(2);
    156 assertKind(elements_kind.fast, obj);
    157 
    158 // Case: make sure transitions from packed to holey are tracked
    159 function fastliteralcase_smiholey(index, value) {
    160   var literal = [1, 2, 3, 4];
    161   literal[index] = value;
    162   return literal;
    163 }
    164 
    165 obj = fastliteralcase_smiholey(5, 1);
    166 assertKind(elements_kind.fast_smi_only, obj);
    167 assertHoley(obj);
    168 obj = fastliteralcase_smiholey(0, 1);
    169 assertKind(elements_kind.fast_smi_only, obj);
    170 assertHoley(obj);
    171 
    172 function newarraycase_smidouble(value) {
    173   var a = new Array();
    174   a[0] = value;
    175   return a;
    176 }
    177 
    178 // Case: new Array() as allocation site, smi->double
    179 obj = newarraycase_smidouble(1);
    180 assertKind(elements_kind.fast_smi_only, obj);
    181 obj = newarraycase_smidouble(1.5);
    182 assertKind(elements_kind.fast_double, obj);
    183 obj = newarraycase_smidouble(2);
    184 assertKind(elements_kind.fast_double, obj);
    185 
    186 function newarraycase_smiobj(value) {
    187   var a = new Array();
    188   a[0] = value;
    189   return a;
    190 }
    191 
    192 // Case: new Array() as allocation site, smi->fast
    193 obj = newarraycase_smiobj(1);
    194 assertKind(elements_kind.fast_smi_only, obj);
    195 obj = newarraycase_smiobj("gloria");
    196 assertKind(elements_kind.fast, obj);
    197 obj = newarraycase_smiobj(2);
    198 assertKind(elements_kind.fast, obj);
    199 
    200 function newarraycase_length_smidouble(value) {
    201   var a = new Array(3);
    202   a[0] = value;
    203   return a;
    204 }
    205 
    206 // Case: new Array(length) as allocation site
    207 obj = newarraycase_length_smidouble(1);
    208 assertKind(elements_kind.fast_smi_only, obj);
    209 obj = newarraycase_length_smidouble(1.5);
    210 assertKind(elements_kind.fast_double, obj);
    211 obj = newarraycase_length_smidouble(2);
    212 assertKind(elements_kind.fast_double, obj);
    213 
    214 // Try to continue the transition to fast object.
    215 // TODO(mvstanton): re-enable commented out code when
    216 // FLAG_pretenuring_call_new is turned on in the build.
    217 obj = newarraycase_length_smidouble("coates");
    218 assertKind(elements_kind.fast, obj);
    219 obj = newarraycase_length_smidouble(2);
    220 // assertKind(elements_kind.fast, obj);
    221 
    222 function newarraycase_length_smiobj(value) {
    223   var a = new Array(3);
    224   a[0] = value;
    225   return a;
    226 }
    227 
    228 // Case: new Array(<length>) as allocation site, smi->fast
    229 obj = newarraycase_length_smiobj(1);
    230 assertKind(elements_kind.fast_smi_only, obj);
    231 obj = newarraycase_length_smiobj("gloria");
    232 assertKind(elements_kind.fast, obj);
    233 obj = newarraycase_length_smiobj(2);
    234 assertKind(elements_kind.fast, obj);
    235 
    236 function newarraycase_list_smidouble(value) {
    237   var a = new Array(1, 2, 3);
    238   a[0] = value;
    239   return a;
    240 }
    241 
    242 obj = newarraycase_list_smidouble(1);
    243 assertKind(elements_kind.fast_smi_only, obj);
    244 obj = newarraycase_list_smidouble(1.5);
    245 assertKind(elements_kind.fast_double, obj);
    246 obj = newarraycase_list_smidouble(2);
    247 assertKind(elements_kind.fast_double, obj);
    248 
    249 function newarraycase_list_smiobj(value) {
    250   var a = new Array(4, 5, 6);
    251   a[0] = value;
    252   return a;
    253 }
    254 
    255 obj = newarraycase_list_smiobj(1);
    256 assertKind(elements_kind.fast_smi_only, obj);
    257 obj = newarraycase_list_smiobj("coates");
    258 assertKind(elements_kind.fast, obj);
    259 obj = newarraycase_list_smiobj(2);
    260 assertKind(elements_kind.fast, obj);
    261 
    262 // Case: array constructor calls with out of date feedback.
    263 // The boilerplate should incorporate all feedback, but the input array
    264 // should be minimally transitioned based on immediate need.
    265 (function() {
    266   function foo(i) {
    267     // We have two cases, one for literals one for constructed arrays.
    268     var a = (i == 0)
    269       ? [1, 2, 3]
    270       : new Array(1, 2, 3);
    271     return a;
    272   }
    273 
    274   for (i = 0; i < 2; i++) {
    275     a = foo(i);
    276     b = foo(i);
    277     b[5] = 1;  // boilerplate goes holey
    278     assertHoley(foo(i));
    279     a[0] = 3.5;  // boilerplate goes holey double
    280     assertKind(elements_kind.fast_double, a);
    281     assertNotHoley(a);
    282     c = foo(i);
    283     assertKind(elements_kind.fast_double, c);
    284     assertHoley(c);
    285   }
    286 })();
    287 
    288 function newarraycase_onearg(len, value) {
    289   var a = new Array(len);
    290   a[0] = value;
    291   return a;
    292 }
    293 
    294 obj = newarraycase_onearg(5, 3.5);
    295 assertKind(elements_kind.fast_double, obj);
    296 obj = newarraycase_onearg(10, 5);
    297 assertKind(elements_kind.fast_double, obj);
    298 obj = newarraycase_onearg(0, 5);
    299 assertKind(elements_kind.fast_double, obj);
    300 
    301 // Verify that cross context calls work
    302 var realmA = Realm.current();
    303 var realmB = Realm.create();
    304 assertEquals(0, realmA);
    305 assertEquals(1, realmB);
    306 
    307 function instanceof_check(type) {
    308   assertTrue(new type() instanceof type);
    309   assertTrue(new type(5) instanceof type);
    310   assertTrue(new type(1,2,3) instanceof type);
    311 }
    312 
    313 function instanceof_check2(type) {
    314   assertTrue(new type() instanceof type);
    315   assertTrue(new type(5) instanceof type);
    316   assertTrue(new type(1,2,3) instanceof type);
    317 }
    318 
    319 var realmBArray = Realm.eval(realmB, "Array");
    320 instanceof_check(Array);
    321 instanceof_check(realmBArray);
    322 
    323 // instanceof_check2 is here because the call site goes through a state.
    324 // Since instanceof_check(Array) was first called with the current context
    325 // Array function, it went from (uninit->Array) then (Array->megamorphic).
    326 // We'll get a different state traversal if we start with realmBArray.
    327 // It'll go (uninit->realmBArray) then (realmBArray->megamorphic). Recognize
    328 // that state "Array" implies an AllocationSite is present, and code is
    329 // configured to use it.
    330 instanceof_check2(realmBArray);
    331 instanceof_check2(Array);
    332 
    333   %OptimizeFunctionOnNextCall(instanceof_check);
    334 
    335 // No de-opt will occur because HCallNewArray wasn't selected, on account of
    336 // the call site not being monomorphic to Array.
    337 instanceof_check(Array);
    338 assertOptimized(instanceof_check);
    339 instanceof_check(realmBArray);
    340 assertOptimized(instanceof_check);
    341 
    342 // Try to optimize again, but first clear all type feedback, and allow it
    343 // to be monomorphic on first call. Only after crankshafting do we introduce
    344 // realmBArray. This should deopt the method.
    345   %DeoptimizeFunction(instanceof_check);
    346   %ClearFunctionTypeFeedback(instanceof_check);
    347 instanceof_check(Array);
    348 instanceof_check(Array);
    349   %OptimizeFunctionOnNextCall(instanceof_check);
    350 instanceof_check(Array);
    351 assertOptimized(instanceof_check);
    352 
    353 instanceof_check(realmBArray);
    354 assertUnoptimized(instanceof_check);
    355 
    356 // Case: make sure nested arrays benefit from allocation site feedback as
    357 // well.
    358 (function() {
    359   // Make sure we handle nested arrays
    360   function get_nested_literal() {
    361     var literal = [[1,2,3,4], [2], [3]];
    362     return literal;
    363   }
    364 
    365   obj = get_nested_literal();
    366   assertKind(elements_kind.fast, obj);
    367   obj[0][0] = 3.5;
    368   obj[2][0] = "hello";
    369   obj = get_nested_literal();
    370   assertKind(elements_kind.fast_double, obj[0]);
    371   assertKind(elements_kind.fast_smi_only, obj[1]);
    372   assertKind(elements_kind.fast, obj[2]);
    373 
    374   // A more complex nested literal case.
    375   function get_deep_nested_literal() {
    376     var literal = [[1], [[2], "hello"], 3, [4]];
    377     return literal;
    378   }
    379 
    380   obj = get_deep_nested_literal();
    381   assertKind(elements_kind.fast_smi_only, obj[1][0]);
    382   obj[0][0] = 3.5;
    383   obj[1][0][0] = "goodbye";
    384   assertKind(elements_kind.fast_double, obj[0]);
    385   assertKind(elements_kind.fast, obj[1][0]);
    386 
    387   obj = get_deep_nested_literal();
    388   assertKind(elements_kind.fast_double, obj[0]);
    389   assertKind(elements_kind.fast, obj[1][0]);
    390 })();
    391 
    392 // Perform a gc because without it the test below can experience an
    393 // allocation failure at an inconvenient point. Allocation mementos get
    394 // cleared on gc, and they can't deliver elements kind feedback when that
    395 // happens.
    396 gc();
    397 
    398 // Make sure object literals with array fields benefit from the type feedback
    399 // that allocation mementos provide.
    400 (function() {
    401   // A literal in an object
    402   function get_object_literal() {
    403     var literal = {
    404       array: [1,2,3],
    405       data: 3.5
    406     };
    407     return literal;
    408   }
    409 
    410   obj = get_object_literal();
    411   assertKind(elements_kind.fast_smi_only, obj.array);
    412   obj.array[1] = 3.5;
    413   assertKind(elements_kind.fast_double, obj.array);
    414   obj = get_object_literal();
    415   assertKind(elements_kind.fast_double, obj.array);
    416 
    417   function get_nested_object_literal() {
    418     var literal = {
    419       array: [[1],[2],[3]],
    420       data: 3.5
    421     };
    422     return literal;
    423   }
    424 
    425   obj = get_nested_object_literal();
    426   assertKind(elements_kind.fast, obj.array);
    427   assertKind(elements_kind.fast_smi_only, obj.array[1]);
    428   obj.array[1][0] = 3.5;
    429   assertKind(elements_kind.fast_double, obj.array[1]);
    430   obj = get_nested_object_literal();
    431   assertKind(elements_kind.fast_double, obj.array[1]);
    432 
    433     %OptimizeFunctionOnNextCall(get_nested_object_literal);
    434   get_nested_object_literal();
    435   obj = get_nested_object_literal();
    436   assertKind(elements_kind.fast_double, obj.array[1]);
    437 
    438   // Make sure we handle nested arrays
    439   function get_nested_literal() {
    440     var literal = [[1,2,3,4], [2], [3]];
    441     return literal;
    442   }
    443 
    444   obj = get_nested_literal();
    445   assertKind(elements_kind.fast, obj);
    446   obj[0][0] = 3.5;
    447   obj[2][0] = "hello";
    448   obj = get_nested_literal();
    449   assertKind(elements_kind.fast_double, obj[0]);
    450   assertKind(elements_kind.fast_smi_only, obj[1]);
    451   assertKind(elements_kind.fast, obj[2]);
    452 
    453   // A more complex nested literal case.
    454   function get_deep_nested_literal() {
    455     var literal = [[1], [[2], "hello"], 3, [4]];
    456     return literal;
    457   }
    458 
    459   obj = get_deep_nested_literal();
    460   assertKind(elements_kind.fast_smi_only, obj[1][0]);
    461   obj[0][0] = 3.5;
    462   obj[1][0][0] = "goodbye";
    463   assertKind(elements_kind.fast_double, obj[0]);
    464   assertKind(elements_kind.fast, obj[1][0]);
    465 
    466   obj = get_deep_nested_literal();
    467   assertKind(elements_kind.fast_double, obj[0]);
    468   assertKind(elements_kind.fast, obj[1][0]);
    469 })();
    470