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 // Two calls with Array because ES6 instanceof desugars into a load of Array,
    321 // and load has a premonomorphic state.
    322 instanceof_check(Array);
    323 instanceof_check(Array);
    324 instanceof_check(realmBArray);
    325 
    326 // instanceof_check2 is here because the call site goes through a state.
    327 // Since instanceof_check(Array) was first called with the current context
    328 // Array function, it went from (uninit->Array) then (Array->megamorphic).
    329 // We'll get a different state traversal if we start with realmBArray.
    330 // It'll go (uninit->realmBArray) then (realmBArray->megamorphic). Recognize
    331 // that state "Array" implies an AllocationSite is present, and code is
    332 // configured to use it.
    333 
    334 // Two calls with realmBArray because ES6 instanceof desugars into a load of
    335 // realmBArray, and load has a premonomorphic state.
    336 instanceof_check2(realmBArray);
    337 instanceof_check2(realmBArray);
    338 instanceof_check2(Array);
    339 
    340   %OptimizeFunctionOnNextCall(instanceof_check);
    341 
    342 // No de-opt will occur because HCallNewArray wasn't selected, on account of
    343 // the call site not being monomorphic to Array.
    344 instanceof_check(Array);
    345 assertOptimized(instanceof_check);
    346 instanceof_check(realmBArray);
    347 assertOptimized(instanceof_check);
    348 
    349 // Try to optimize again, but first clear all type feedback, and allow it
    350 // to be monomorphic on first call. Only after crankshafting do we introduce
    351 // realmBArray. This should deopt the method.
    352   %DeoptimizeFunction(instanceof_check);
    353   %ClearFunctionTypeFeedback(instanceof_check);
    354 instanceof_check(Array);
    355 instanceof_check(Array);
    356   %OptimizeFunctionOnNextCall(instanceof_check);
    357 instanceof_check(Array);
    358 assertOptimized(instanceof_check);
    359 
    360 instanceof_check(realmBArray);
    361 assertUnoptimized(instanceof_check);
    362 
    363 // Case: make sure nested arrays benefit from allocation site feedback as
    364 // well.
    365 (function() {
    366   // Make sure we handle nested arrays
    367   function get_nested_literal() {
    368     var literal = [[1,2,3,4], [2], [3]];
    369     return literal;
    370   }
    371 
    372   obj = get_nested_literal();
    373   assertKind(elements_kind.fast, obj);
    374   obj[0][0] = 3.5;
    375   obj[2][0] = "hello";
    376   obj = get_nested_literal();
    377   assertKind(elements_kind.fast_double, obj[0]);
    378   assertKind(elements_kind.fast_smi_only, obj[1]);
    379   assertKind(elements_kind.fast, obj[2]);
    380 
    381   // A more complex nested literal case.
    382   function get_deep_nested_literal() {
    383     var literal = [[1], [[2], "hello"], 3, [4]];
    384     return literal;
    385   }
    386 
    387   obj = get_deep_nested_literal();
    388   assertKind(elements_kind.fast_smi_only, obj[1][0]);
    389   obj[0][0] = 3.5;
    390   obj[1][0][0] = "goodbye";
    391   assertKind(elements_kind.fast_double, obj[0]);
    392   assertKind(elements_kind.fast, obj[1][0]);
    393 
    394   obj = get_deep_nested_literal();
    395   assertKind(elements_kind.fast_double, obj[0]);
    396   assertKind(elements_kind.fast, obj[1][0]);
    397 })();
    398 
    399 // Perform a gc because without it the test below can experience an
    400 // allocation failure at an inconvenient point. Allocation mementos get
    401 // cleared on gc, and they can't deliver elements kind feedback when that
    402 // happens.
    403 gc();
    404 
    405 // Make sure object literals with array fields benefit from the type feedback
    406 // that allocation mementos provide.
    407 (function() {
    408   // A literal in an object
    409   function get_object_literal() {
    410     var literal = {
    411       array: [1,2,3],
    412       data: 3.5
    413     };
    414     return literal;
    415   }
    416 
    417   obj = get_object_literal();
    418   assertKind(elements_kind.fast_smi_only, obj.array);
    419   obj.array[1] = 3.5;
    420   assertKind(elements_kind.fast_double, obj.array);
    421   obj = get_object_literal();
    422   assertKind(elements_kind.fast_double, obj.array);
    423 
    424   function get_nested_object_literal() {
    425     var literal = {
    426       array: [[1],[2],[3]],
    427       data: 3.5
    428     };
    429     return literal;
    430   }
    431 
    432   obj = get_nested_object_literal();
    433   assertKind(elements_kind.fast, obj.array);
    434   assertKind(elements_kind.fast_smi_only, obj.array[1]);
    435   obj.array[1][0] = 3.5;
    436   assertKind(elements_kind.fast_double, obj.array[1]);
    437   obj = get_nested_object_literal();
    438   assertKind(elements_kind.fast_double, obj.array[1]);
    439 
    440     %OptimizeFunctionOnNextCall(get_nested_object_literal);
    441   get_nested_object_literal();
    442   obj = get_nested_object_literal();
    443   assertKind(elements_kind.fast_double, obj.array[1]);
    444 
    445   // Make sure we handle nested arrays
    446   function get_nested_literal() {
    447     var literal = [[1,2,3,4], [2], [3]];
    448     return literal;
    449   }
    450 
    451   obj = get_nested_literal();
    452   assertKind(elements_kind.fast, obj);
    453   obj[0][0] = 3.5;
    454   obj[2][0] = "hello";
    455   obj = get_nested_literal();
    456   assertKind(elements_kind.fast_double, obj[0]);
    457   assertKind(elements_kind.fast_smi_only, obj[1]);
    458   assertKind(elements_kind.fast, obj[2]);
    459 
    460   // A more complex nested literal case.
    461   function get_deep_nested_literal() {
    462     var literal = [[1], [[2], "hello"], 3, [4]];
    463     return literal;
    464   }
    465 
    466   obj = get_deep_nested_literal();
    467   assertKind(elements_kind.fast_smi_only, obj[1][0]);
    468   obj[0][0] = 3.5;
    469   obj[1][0][0] = "goodbye";
    470   assertKind(elements_kind.fast_double, obj[0]);
    471   assertKind(elements_kind.fast, obj[1][0]);
    472 
    473   obj = get_deep_nested_literal();
    474   assertKind(elements_kind.fast_double, obj[0]);
    475   assertKind(elements_kind.fast, obj[1][0]);
    476 })();
    477 
    478 // Test gathering allocation site feedback for generic ics.
    479 (function() {
    480   function make() { return new Array(); }
    481   function foo(a, i) { a[0] = i; }
    482 
    483   var a = make();
    484   assertKind(elements_kind.fast_smi_only, a);
    485 
    486   // Make the keyed store ic go generic.
    487   foo("howdy", 1);
    488   foo(a, 3.5);
    489 
    490   var b = make();
    491   assertKind(elements_kind.fast_double, b);
    492 })();
    493