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