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 --nostress-opt --typed-array-max_size_in-heap=2048 29 30 // Test element kind of objects. 31 // Since --smi-only-arrays affects builtins, its default setting at compile 32 // time sticks if built with snapshot. If --smi-only-arrays is deactivated 33 // by default, only a no-snapshot build actually has smi-only arrays enabled 34 // in this test case. Depending on whether smi-only arrays are actually 35 // enabled, this test takes the appropriate code path to check smi-only arrays. 36 37 support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8)); 38 39 if (support_smi_only_arrays) { 40 print("Tests include smi-only arrays."); 41 } else { 42 print("Tests do NOT include smi-only arrays."); 43 } 44 45 var elements_kind = { 46 fast_smi_only : 'fast smi only elements', 47 fast : 'fast elements', 48 fast_double : 'fast double elements', 49 dictionary : 'dictionary elements', 50 external_int32 : 'external int8 elements', 51 external_uint8 : 'external uint8 elements', 52 external_int16 : 'external int16 elements', 53 external_uint16 : 'external uint16 elements', 54 external_int32 : 'external int32 elements', 55 external_uint32 : 'external uint32 elements', 56 external_float32 : 'external float32 elements', 57 external_float64 : 'external float64 elements', 58 external_uint8_clamped : 'external uint8_clamped elements', 59 fixed_int32 : 'fixed int8 elements', 60 fixed_uint8 : 'fixed uint8 elements', 61 fixed_int16 : 'fixed int16 elements', 62 fixed_uint16 : 'fixed uint16 elements', 63 fixed_int32 : 'fixed int32 elements', 64 fixed_uint32 : 'fixed uint32 elements', 65 fixed_float32 : 'fixed float32 elements', 66 fixed_float64 : 'fixed float64 elements', 67 fixed_uint8_clamped : 'fixed uint8_clamped elements' 68 } 69 70 function getKind(obj) { 71 if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only; 72 if (%HasFastObjectElements(obj)) return elements_kind.fast; 73 if (%HasFastDoubleElements(obj)) return elements_kind.fast_double; 74 if (%HasDictionaryElements(obj)) return elements_kind.dictionary; 75 76 // Every external kind is also an external array. 77 if (%HasExternalInt8Elements(obj)) { 78 return elements_kind.external_int8; 79 } 80 if (%HasExternalUint8Elements(obj)) { 81 return elements_kind.external_uint8; 82 } 83 if (%HasExternalInt16Elements(obj)) { 84 return elements_kind.external_int16; 85 } 86 if (%HasExternalUint16Elements(obj)) { 87 return elements_kind.external_uint16; 88 } 89 if (%HasExternalInt32Elements(obj)) { 90 return elements_kind.external_int32; 91 } 92 if (%HasExternalUint32Elements(obj)) { 93 return elements_kind.external_uint32; 94 } 95 if (%HasExternalFloat32Elements(obj)) { 96 return elements_kind.external_float32; 97 } 98 if (%HasExternalFloat64Elements(obj)) { 99 return elements_kind.external_float64; 100 } 101 if (%HasExternalUint8ClampedElements(obj)) { 102 return elements_kind.external_uint8_clamped; 103 } 104 if (%HasFixedInt8Elements(obj)) { 105 return elements_kind.fixed_int8; 106 } 107 if (%HasFixedUint8Elements(obj)) { 108 return elements_kind.fixed_uint8; 109 } 110 if (%HasFixedInt16Elements(obj)) { 111 return elements_kind.fixed_int16; 112 } 113 if (%HasFixedUint16Elements(obj)) { 114 return elements_kind.fixed_uint16; 115 } 116 if (%HasFixedInt32Elements(obj)) { 117 return elements_kind.fixed_int32; 118 } 119 if (%HasFixedUint32Elements(obj)) { 120 return elements_kind.fixed_uint32; 121 } 122 if (%HasFixedFloat32Elements(obj)) { 123 return elements_kind.fixed_float32; 124 } 125 if (%HasFixedFloat64Elements(obj)) { 126 return elements_kind.fixed_float64; 127 } 128 if (%HasFixedUint8ClampedElements(obj)) { 129 return elements_kind.fixed_uint8_clamped; 130 } 131 } 132 133 function assertKind(expected, obj, name_opt) { 134 if (!support_smi_only_arrays && 135 expected == elements_kind.fast_smi_only) { 136 expected = elements_kind.fast; 137 } 138 assertEquals(expected, getKind(obj), name_opt); 139 } 140 141 var me = {}; 142 assertKind(elements_kind.fast, me); 143 me.dance = 0xD15C0; 144 me.drink = 0xC0C0A; 145 assertKind(elements_kind.fast, me); 146 147 if (support_smi_only_arrays) { 148 var too = [1,2,3]; 149 assertKind(elements_kind.fast_smi_only, too); 150 too.dance = 0xD15C0; 151 too.drink = 0xC0C0A; 152 assertKind(elements_kind.fast_smi_only, too); 153 } 154 155 // Make sure the element kind transitions from smi when a non-smi is stored. 156 function test_wrapper() { 157 var you = new Array(); 158 assertKind(elements_kind.fast_smi_only, you); 159 for (var i = 0; i < 1337; i++) { 160 var val = i; 161 if (i == 1336) { 162 assertKind(elements_kind.fast_smi_only, you); 163 val = new Object(); 164 } 165 you[i] = val; 166 } 167 assertKind(elements_kind.fast, you); 168 169 assertKind(elements_kind.dictionary, new Array(0xDECAF)); 170 171 var fast_double_array = new Array(0xDECAF); 172 for (var i = 0; i < 0xDECAF; i++) fast_double_array[i] = i / 2; 173 assertKind(elements_kind.fast_double, fast_double_array); 174 175 assertKind(elements_kind.fixed_int8, new Int8Array(007)); 176 assertKind(elements_kind.fixed_uint8, new Uint8Array(007)); 177 assertKind(elements_kind.fixed_int16, new Int16Array(666)); 178 assertKind(elements_kind.fixed_uint16, new Uint16Array(42)); 179 assertKind(elements_kind.fixed_int32, new Int32Array(0xF)); 180 assertKind(elements_kind.fixed_uint32, new Uint32Array(23)); 181 assertKind(elements_kind.fixed_float32, new Float32Array(7)); 182 assertKind(elements_kind.fixed_float64, new Float64Array(0)); 183 assertKind(elements_kind.fixed_uint8_clamped, new Uint8ClampedArray(512)); 184 185 var ab = new ArrayBuffer(128); 186 assertKind(elements_kind.external_int8, new Int8Array(ab)); 187 assertKind(elements_kind.external_uint8, new Uint8Array(ab)); 188 assertKind(elements_kind.external_int16, new Int16Array(ab)); 189 assertKind(elements_kind.external_uint16, new Uint16Array(ab)); 190 assertKind(elements_kind.external_int32, new Int32Array(ab)); 191 assertKind(elements_kind.external_uint32, new Uint32Array(ab)); 192 assertKind(elements_kind.external_float32, new Float32Array(ab)); 193 assertKind(elements_kind.external_float64, new Float64Array(ab)); 194 assertKind(elements_kind.external_uint8_clamped, new Uint8ClampedArray(ab)); 195 196 // Crankshaft support for smi-only array elements. 197 function monomorphic(array) { 198 assertKind(elements_kind.fast_smi_only, array); 199 for (var i = 0; i < 3; i++) { 200 array[i] = i + 10; 201 } 202 assertKind(elements_kind.fast_smi_only, array); 203 for (var i = 0; i < 3; i++) { 204 var a = array[i]; 205 assertEquals(i + 10, a); 206 } 207 } 208 var smi_only = new Array(1, 2, 3); 209 assertKind(elements_kind.fast_smi_only, smi_only); 210 for (var i = 0; i < 3; i++) monomorphic(smi_only); 211 %OptimizeFunctionOnNextCall(monomorphic); 212 monomorphic(smi_only); 213 } 214 215 // The test is called in a wrapper function to eliminate the transition learning 216 // feedback of AllocationSites. 217 test_wrapper(); 218 %ClearFunctionTypeFeedback(test_wrapper); 219 220 if (support_smi_only_arrays) { 221 %NeverOptimizeFunction(construct_smis); 222 223 // This code exists to eliminate the learning influence of AllocationSites 224 // on the following tests. 225 var __sequence = 0; 226 function make_array_string() { 227 this.__sequence = this.__sequence + 1; 228 return "/* " + this.__sequence + " */ [0, 0, 0];" 229 } 230 function make_array() { 231 return eval(make_array_string()); 232 } 233 234 function construct_smis() { 235 var a = make_array(); 236 a[0] = 0; // Send the COW array map to the steak house. 237 assertKind(elements_kind.fast_smi_only, a); 238 return a; 239 } 240 %NeverOptimizeFunction(construct_doubles); 241 function construct_doubles() { 242 var a = construct_smis(); 243 a[0] = 1.5; 244 assertKind(elements_kind.fast_double, a); 245 return a; 246 } 247 %NeverOptimizeFunction(construct_objects); 248 function construct_objects() { 249 var a = construct_smis(); 250 a[0] = "one"; 251 assertKind(elements_kind.fast, a); 252 return a; 253 } 254 255 // Test crankshafted transition SMI->DOUBLE. 256 %NeverOptimizeFunction(convert_to_double); 257 function convert_to_double(array) { 258 array[1] = 2.5; 259 assertKind(elements_kind.fast_double, array); 260 assertEquals(2.5, array[1]); 261 } 262 var smis = construct_smis(); 263 for (var i = 0; i < 3; i++) convert_to_double(smis); 264 %OptimizeFunctionOnNextCall(convert_to_double); 265 smis = construct_smis(); 266 convert_to_double(smis); 267 // Test crankshafted transitions SMI->FAST and DOUBLE->FAST. 268 %NeverOptimizeFunction(convert_to_fast); 269 function convert_to_fast(array) { 270 array[1] = "two"; 271 assertKind(elements_kind.fast, array); 272 assertEquals("two", array[1]); 273 } 274 smis = construct_smis(); 275 for (var i = 0; i < 3; i++) convert_to_fast(smis); 276 var doubles = construct_doubles(); 277 for (var i = 0; i < 3; i++) convert_to_fast(doubles); 278 smis = construct_smis(); 279 doubles = construct_doubles(); 280 %OptimizeFunctionOnNextCall(convert_to_fast); 281 convert_to_fast(smis); 282 convert_to_fast(doubles); 283 // Test transition chain SMI->DOUBLE->FAST (crankshafted function will 284 // transition to FAST directly). 285 %NeverOptimizeFunction(convert_mixed); 286 function convert_mixed(array, value, kind) { 287 array[1] = value; 288 assertKind(kind, array); 289 assertEquals(value, array[1]); 290 } 291 smis = construct_smis(); 292 for (var i = 0; i < 3; i++) { 293 convert_mixed(smis, 1.5, elements_kind.fast_double); 294 } 295 doubles = construct_doubles(); 296 for (var i = 0; i < 3; i++) { 297 convert_mixed(doubles, "three", elements_kind.fast); 298 } 299 convert_mixed(construct_smis(), "three", elements_kind.fast); 300 convert_mixed(construct_doubles(), "three", elements_kind.fast); 301 %OptimizeFunctionOnNextCall(convert_mixed); 302 smis = construct_smis(); 303 doubles = construct_doubles(); 304 convert_mixed(smis, 1, elements_kind.fast); 305 convert_mixed(doubles, 1, elements_kind.fast); 306 assertTrue(%HaveSameMap(smis, doubles)); 307 } 308 309 // Crankshaft support for smi-only elements in dynamic array literals. 310 function get(foo) { return foo; } // Used to generate dynamic values. 311 312 function crankshaft_test() { 313 if (support_smi_only_arrays) { 314 var a1 = [get(1), get(2), get(3)]; 315 assertKind(elements_kind.fast_smi_only, a1); 316 } 317 var a2 = new Array(get(1), get(2), get(3)); 318 assertKind(elements_kind.fast_smi_only, a2); 319 var b = [get(1), get(2), get("three")]; 320 assertKind(elements_kind.fast, b); 321 var c = [get(1), get(2), get(3.5)]; 322 if (support_smi_only_arrays) { 323 assertKind(elements_kind.fast_double, c); 324 } 325 } 326 for (var i = 0; i < 3; i++) { 327 crankshaft_test(); 328 } 329 %OptimizeFunctionOnNextCall(crankshaft_test); 330 crankshaft_test(); 331 332 // Elements_kind transitions for arrays. 333 334 // A map can have three different elements_kind transitions: SMI->DOUBLE, 335 // DOUBLE->OBJECT, and SMI->OBJECT. No matter in which order these three are 336 // created, they must always end up with the same FAST map. 337 338 // This test is meaningless without FAST_SMI_ONLY_ELEMENTS. 339 if (support_smi_only_arrays) { 340 // Preparation: create one pair of identical objects for each case. 341 var a = [1, 2, 3]; 342 var b = [1, 2, 3]; 343 assertTrue(%HaveSameMap(a, b)); 344 assertKind(elements_kind.fast_smi_only, a); 345 var c = [1, 2, 3]; 346 c["case2"] = true; 347 var d = [1, 2, 3]; 348 d["case2"] = true; 349 assertTrue(%HaveSameMap(c, d)); 350 assertFalse(%HaveSameMap(a, c)); 351 assertKind(elements_kind.fast_smi_only, c); 352 var e = [1, 2, 3]; 353 e["case3"] = true; 354 var f = [1, 2, 3]; 355 f["case3"] = true; 356 assertTrue(%HaveSameMap(e, f)); 357 assertFalse(%HaveSameMap(a, e)); 358 assertFalse(%HaveSameMap(c, e)); 359 assertKind(elements_kind.fast_smi_only, e); 360 // Case 1: SMI->DOUBLE, DOUBLE->OBJECT, SMI->OBJECT. 361 a[0] = 1.5; 362 assertKind(elements_kind.fast_double, a); 363 a[0] = "foo"; 364 assertKind(elements_kind.fast, a); 365 b[0] = "bar"; 366 assertTrue(%HaveSameMap(a, b)); 367 // Case 2: SMI->DOUBLE, SMI->OBJECT, DOUBLE->OBJECT. 368 c[0] = 1.5; 369 assertKind(elements_kind.fast_double, c); 370 assertFalse(%HaveSameMap(c, d)); 371 d[0] = "foo"; 372 assertKind(elements_kind.fast, d); 373 assertFalse(%HaveSameMap(c, d)); 374 c[0] = "bar"; 375 assertTrue(%HaveSameMap(c, d)); 376 // Case 3: SMI->OBJECT, SMI->DOUBLE, DOUBLE->OBJECT. 377 e[0] = "foo"; 378 assertKind(elements_kind.fast, e); 379 assertFalse(%HaveSameMap(e, f)); 380 f[0] = 1.5; 381 assertKind(elements_kind.fast_double, f); 382 assertFalse(%HaveSameMap(e, f)); 383 f[0] = "bar"; 384 assertKind(elements_kind.fast, f); 385 assertTrue(%HaveSameMap(e, f)); 386 } 387 388 // Test if Array.concat() works correctly with DOUBLE elements. 389 if (support_smi_only_arrays) { 390 var a = [1, 2]; 391 assertKind(elements_kind.fast_smi_only, a); 392 var b = [4.5, 5.5]; 393 assertKind(elements_kind.fast_double, b); 394 var c = a.concat(b); 395 assertEquals([1, 2, 4.5, 5.5], c); 396 assertKind(elements_kind.fast_double, c); 397 } 398 399 // Test that Array.push() correctly handles SMI elements. 400 if (support_smi_only_arrays) { 401 var a = [1, 2]; 402 assertKind(elements_kind.fast_smi_only, a); 403 a.push(3, 4, 5); 404 assertKind(elements_kind.fast_smi_only, a); 405 assertEquals([1, 2, 3, 4, 5], a); 406 } 407 408 // Test that Array.splice() and Array.slice() return correct ElementsKinds. 409 if (support_smi_only_arrays) { 410 var a = ["foo", "bar"]; 411 assertKind(elements_kind.fast, a); 412 var b = a.splice(0, 1); 413 assertKind(elements_kind.fast, b); 414 var c = a.slice(0, 1); 415 assertKind(elements_kind.fast, c); 416 } 417 418 // Throw away type information in the ICs for next stress run. 419 gc(); 420