• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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));
44support_smi_only_arrays = true;
45
46if (support_smi_only_arrays) {
47  print("Tests include smi-only arrays.");
48} else {
49  print("Tests do NOT include smi-only arrays.");
50}
51
52var 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
68function 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
75function isHoley(obj) {
76  if (%HasFastHoleyElements(obj)) return true;
77  return false;
78}
79
80function 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
88function assertHoley(obj, name_opt) {
89  assertEquals(true, isHoley(obj), name_opt);
90}
91
92function assertNotHoley(obj, name_opt) {
93  assertEquals(false, isHoley(obj), name_opt);
94}
95
96if (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