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