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: --notrack_allocation_sites
30
31// Limit the number of stress runs to reduce polymorphism it defeats some of the
32// assumptions made about how elements transitions work because transition stubs
33// end up going generic.
34// Flags: --stress-runs=2
35
36// Test element kind of objects.
37// Since --smi-only-arrays affects builtins, its default setting at compile
38// time sticks if built with snapshot.  If --smi-only-arrays is deactivated
39// by default, only a no-snapshot build actually has smi-only arrays enabled
40// in this test case.  Depending on whether smi-only arrays are actually
41// enabled, this test takes the appropriate code path to check smi-only arrays.
42
43support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8));
44
45if (support_smi_only_arrays) {
46  print("Tests include smi-only arrays.");
47} else {
48  print("Tests do NOT include smi-only arrays.");
49}
50
51var elements_kind = {
52  fast_smi_only            :  'fast smi only elements',
53  fast                     :  'fast elements',
54  fast_double              :  'fast double elements',
55  dictionary               :  'dictionary elements',
56  external_byte            :  'external byte elements',
57  external_unsigned_byte   :  'external unsigned byte elements',
58  external_short           :  'external short elements',
59  external_unsigned_short  :  'external unsigned short elements',
60  external_int             :  'external int elements',
61  external_unsigned_int    :  'external unsigned int elements',
62  external_float           :  'external float elements',
63  external_double          :  'external double elements',
64  external_pixel           :  'external pixel elements'
65}
66
67function getKind(obj) {
68  if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only;
69  if (%HasFastObjectElements(obj)) return elements_kind.fast;
70  if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
71  if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
72  // Every external kind is also an external array.
73  assertTrue(%HasExternalArrayElements(obj));
74  if (%HasExternalByteElements(obj)) {
75    return elements_kind.external_byte;
76  }
77  if (%HasExternalUnsignedByteElements(obj)) {
78    return elements_kind.external_unsigned_byte;
79  }
80  if (%HasExternalShortElements(obj)) {
81    return elements_kind.external_short;
82  }
83  if (%HasExternalUnsignedShortElements(obj)) {
84    return elements_kind.external_unsigned_short;
85  }
86  if (%HasExternalIntElements(obj)) {
87    return elements_kind.external_int;
88  }
89  if (%HasExternalUnsignedIntElements(obj)) {
90    return elements_kind.external_unsigned_int;
91  }
92  if (%HasExternalFloatElements(obj)) {
93    return elements_kind.external_float;
94  }
95  if (%HasExternalDoubleElements(obj)) {
96    return elements_kind.external_double;
97  }
98  if (%HasExternalPixelElements(obj)) {
99    return elements_kind.external_pixel;
100  }
101}
102
103function assertKind(expected, obj, name_opt) {
104  if (!support_smi_only_arrays &&
105      expected == elements_kind.fast_smi_only) {
106    expected = elements_kind.fast;
107  }
108  assertEquals(expected, getKind(obj), name_opt);
109}
110
111var me = {};
112assertKind(elements_kind.fast, me);
113me.dance = 0xD15C0;
114me.drink = 0xC0C0A;
115assertKind(elements_kind.fast, me);
116
117if (support_smi_only_arrays) {
118  var too = [1,2,3];
119  assertKind(elements_kind.fast_smi_only, too);
120  too.dance = 0xD15C0;
121  too.drink = 0xC0C0A;
122  assertKind(elements_kind.fast_smi_only, too);
123}
124
125// Make sure the element kind transitions from smi when a non-smi is stored.
126var you = new Array();
127assertKind(elements_kind.fast_smi_only, you);
128for (var i = 0; i < 1337; i++) {
129  var val = i;
130  if (i == 1336) {
131    assertKind(elements_kind.fast_smi_only, you);
132    val = new Object();
133  }
134  you[i] = val;
135}
136assertKind(elements_kind.fast, you);
137
138assertKind(elements_kind.dictionary, new Array(0xDECAF));
139
140var fast_double_array = new Array(0xDECAF);
141for (var i = 0; i < 0xDECAF; i++) fast_double_array[i] = i / 2;
142assertKind(elements_kind.fast_double, fast_double_array);
143
144assertKind(elements_kind.external_byte,           new Int8Array(9001));
145assertKind(elements_kind.external_unsigned_byte,  new Uint8Array(007));
146assertKind(elements_kind.external_short,          new Int16Array(666));
147assertKind(elements_kind.external_unsigned_short, new Uint16Array(42));
148assertKind(elements_kind.external_int,            new Int32Array(0xF));
149assertKind(elements_kind.external_unsigned_int,   new Uint32Array(23));
150assertKind(elements_kind.external_float,          new Float32Array(7));
151assertKind(elements_kind.external_double,         new Float64Array(0));
152assertKind(elements_kind.external_pixel,          new Uint8ClampedArray(512));
153
154// Crankshaft support for smi-only array elements.
155function monomorphic(array) {
156  assertKind(elements_kind.fast_smi_only, array);
157  for (var i = 0; i < 3; i++) {
158    array[i] = i + 10;
159  }
160  assertKind(elements_kind.fast_smi_only, array);
161  for (var i = 0; i < 3; i++) {
162    var a = array[i];
163    assertEquals(i + 10, a);
164  }
165}
166var smi_only = new Array(1, 2, 3);
167assertKind(elements_kind.fast_smi_only, smi_only);
168for (var i = 0; i < 3; i++) monomorphic(smi_only);
169%OptimizeFunctionOnNextCall(monomorphic);
170monomorphic(smi_only);
171
172if (support_smi_only_arrays) {
173  %NeverOptimizeFunction(construct_smis);
174  function construct_smis() {
175    var a = [0, 0, 0];
176    a[0] = 0;  // Send the COW array map to the steak house.
177    assertKind(elements_kind.fast_smi_only, a);
178    return a;
179  }
180  %NeverOptimizeFunction(construct_doubles);
181  function construct_doubles() {
182    var a = construct_smis();
183    a[0] = 1.5;
184    assertKind(elements_kind.fast_double, a);
185    return a;
186  }
187  %NeverOptimizeFunction(construct_objects);
188  function construct_objects() {
189    var a = construct_smis();
190    a[0] = "one";
191    assertKind(elements_kind.fast, a);
192    return a;
193  }
194
195  // Test crankshafted transition SMI->DOUBLE.
196  %NeverOptimizeFunction(convert_to_double);
197  function convert_to_double(array) {
198    array[1] = 2.5;
199    assertKind(elements_kind.fast_double, array);
200    assertEquals(2.5, array[1]);
201  }
202  var smis = construct_smis();
203  for (var i = 0; i < 3; i++) convert_to_double(smis);
204  %OptimizeFunctionOnNextCall(convert_to_double);
205  smis = construct_smis();
206  convert_to_double(smis);
207  // Test crankshafted transitions SMI->FAST and DOUBLE->FAST.
208  %NeverOptimizeFunction(convert_to_fast);
209  function convert_to_fast(array) {
210    array[1] = "two";
211    assertKind(elements_kind.fast, array);
212    assertEquals("two", array[1]);
213  }
214  smis = construct_smis();
215  for (var i = 0; i < 3; i++) convert_to_fast(smis);
216  var doubles = construct_doubles();
217  for (var i = 0; i < 3; i++) convert_to_fast(doubles);
218  smis = construct_smis();
219  doubles = construct_doubles();
220  %OptimizeFunctionOnNextCall(convert_to_fast);
221  convert_to_fast(smis);
222  convert_to_fast(doubles);
223  // Test transition chain SMI->DOUBLE->FAST (crankshafted function will
224  // transition to FAST directly).
225  %NeverOptimizeFunction(convert_mixed);
226  function convert_mixed(array, value, kind) {
227    array[1] = value;
228    assertKind(kind, array);
229    assertEquals(value, array[1]);
230  }
231  smis = construct_smis();
232  for (var i = 0; i < 3; i++) {
233    convert_mixed(smis, 1.5, elements_kind.fast_double);
234  }
235  doubles = construct_doubles();
236  for (var i = 0; i < 3; i++) {
237    convert_mixed(doubles, "three", elements_kind.fast);
238  }
239  convert_mixed(construct_smis(), "three", elements_kind.fast);
240  convert_mixed(construct_doubles(), "three", elements_kind.fast);
241  %OptimizeFunctionOnNextCall(convert_mixed);
242  smis = construct_smis();
243  doubles = construct_doubles();
244  convert_mixed(smis, 1, elements_kind.fast);
245  convert_mixed(doubles, 1, elements_kind.fast);
246  assertTrue(%HaveSameMap(smis, doubles));
247}
248
249// Crankshaft support for smi-only elements in dynamic array literals.
250function get(foo) { return foo; }  // Used to generate dynamic values.
251
252function crankshaft_test() {
253  if (support_smi_only_arrays) {
254    var a1 = [get(1), get(2), get(3)];
255    assertKind(elements_kind.fast_smi_only, a1);
256  }
257  var a2 = new Array(get(1), get(2), get(3));
258  assertKind(elements_kind.fast_smi_only, a2);
259  var b = [get(1), get(2), get("three")];
260  assertKind(elements_kind.fast, b);
261  var c = [get(1), get(2), get(3.5)];
262  if (support_smi_only_arrays) {
263    assertKind(elements_kind.fast_double, c);
264  }
265}
266for (var i = 0; i < 3; i++) {
267  crankshaft_test();
268}
269%OptimizeFunctionOnNextCall(crankshaft_test);
270crankshaft_test();
271
272// Elements_kind transitions for arrays.
273
274// A map can have three different elements_kind transitions: SMI->DOUBLE,
275// DOUBLE->OBJECT, and SMI->OBJECT. No matter in which order these three are
276// created, they must always end up with the same FAST map.
277
278// This test is meaningless without FAST_SMI_ONLY_ELEMENTS.
279if (support_smi_only_arrays) {
280  // Preparation: create one pair of identical objects for each case.
281  var a = [1, 2, 3];
282  var b = [1, 2, 3];
283  assertTrue(%HaveSameMap(a, b));
284  assertKind(elements_kind.fast_smi_only, a);
285  var c = [1, 2, 3];
286  c["case2"] = true;
287  var d = [1, 2, 3];
288  d["case2"] = true;
289  assertTrue(%HaveSameMap(c, d));
290  assertFalse(%HaveSameMap(a, c));
291  assertKind(elements_kind.fast_smi_only, c);
292  var e = [1, 2, 3];
293  e["case3"] = true;
294  var f = [1, 2, 3];
295  f["case3"] = true;
296  assertTrue(%HaveSameMap(e, f));
297  assertFalse(%HaveSameMap(a, e));
298  assertFalse(%HaveSameMap(c, e));
299  assertKind(elements_kind.fast_smi_only, e);
300  // Case 1: SMI->DOUBLE, DOUBLE->OBJECT, SMI->OBJECT.
301  a[0] = 1.5;
302  assertKind(elements_kind.fast_double, a);
303  a[0] = "foo";
304  assertKind(elements_kind.fast, a);
305  b[0] = "bar";
306  assertTrue(%HaveSameMap(a, b));
307  // Case 2: SMI->DOUBLE, SMI->OBJECT, DOUBLE->OBJECT.
308  c[0] = 1.5;
309  assertKind(elements_kind.fast_double, c);
310  assertFalse(%HaveSameMap(c, d));
311  d[0] = "foo";
312  assertKind(elements_kind.fast, d);
313  assertFalse(%HaveSameMap(c, d));
314  c[0] = "bar";
315  assertTrue(%HaveSameMap(c, d));
316  // Case 3: SMI->OBJECT, SMI->DOUBLE, DOUBLE->OBJECT.
317  e[0] = "foo";
318  assertKind(elements_kind.fast, e);
319  assertFalse(%HaveSameMap(e, f));
320  f[0] = 1.5;
321  assertKind(elements_kind.fast_double, f);
322  assertFalse(%HaveSameMap(e, f));
323  f[0] = "bar";
324  assertKind(elements_kind.fast, f);
325  assertTrue(%HaveSameMap(e, f));
326}
327
328// Test if Array.concat() works correctly with DOUBLE elements.
329if (support_smi_only_arrays) {
330  var a = [1, 2];
331  assertKind(elements_kind.fast_smi_only, a);
332  var b = [4.5, 5.5];
333  assertKind(elements_kind.fast_double, b);
334  var c = a.concat(b);
335  assertEquals([1, 2, 4.5, 5.5], c);
336  assertKind(elements_kind.fast_double, c);
337}
338
339// Test that Array.push() correctly handles SMI elements.
340if (support_smi_only_arrays) {
341  var a = [1, 2];
342  assertKind(elements_kind.fast_smi_only, a);
343  a.push(3, 4, 5);
344  assertKind(elements_kind.fast_smi_only, a);
345  assertEquals([1, 2, 3, 4, 5], a);
346}
347
348// Test that Array.splice() and Array.slice() return correct ElementsKinds.
349if (support_smi_only_arrays) {
350  var a = ["foo", "bar"];
351  assertKind(elements_kind.fast, a);
352  var b = a.splice(0, 1);
353  assertKind(elements_kind.fast, b);
354  var c = a.slice(0, 1);
355  assertKind(elements_kind.fast, c);
356}
357
358// Throw away type information in the ICs for next stress run.
359gc();
360