Stop overallocating feedback vector slots.
[platform/upstream/v8.git] / test / cctest / test-feedback-vector.cc
1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/v8.h"
6 #include "test/cctest/cctest.h"
7
8 #include "src/api.h"
9 #include "src/debug.h"
10 #include "src/execution.h"
11 #include "src/factory.h"
12 #include "src/global-handles.h"
13 #include "src/macro-assembler.h"
14 #include "src/objects.h"
15
16 using namespace v8::internal;
17
18 namespace {
19
20 TEST(VectorStructure) {
21   LocalContext context;
22   v8::HandleScope scope(context->GetIsolate());
23   Isolate* isolate = CcTest::i_isolate();
24   Factory* factory = isolate->factory();
25   Zone* zone = isolate->runtime_zone();
26
27   // Empty vectors are the empty fixed array.
28   FeedbackVectorSpec empty;
29   Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&empty);
30   CHECK(Handle<FixedArray>::cast(vector)
31             .is_identical_to(factory->empty_fixed_array()));
32   // Which can nonetheless be queried.
33   CHECK_EQ(0, vector->ic_with_type_info_count());
34   CHECK_EQ(0, vector->ic_generic_count());
35   CHECK_EQ(0, vector->Slots());
36   CHECK_EQ(0, vector->ICSlots());
37
38   FeedbackVectorSpec one_slot(1);
39   vector = factory->NewTypeFeedbackVector(&one_slot);
40   CHECK_EQ(1, vector->Slots());
41   CHECK_EQ(0, vector->ICSlots());
42
43   FeedbackVectorSpec one_icslot(0, Code::CALL_IC);
44   vector = factory->NewTypeFeedbackVector(&one_icslot);
45   CHECK_EQ(0, vector->Slots());
46   CHECK_EQ(1, vector->ICSlots());
47
48   ZoneFeedbackVectorSpec spec(zone, 3, 5);
49   for (int i = 0; i < 5; i++) spec.SetKind(i, Code::CALL_IC);
50   vector = factory->NewTypeFeedbackVector(&spec);
51   CHECK_EQ(3, vector->Slots());
52   CHECK_EQ(5, vector->ICSlots());
53
54   int metadata_length = vector->ic_metadata_length();
55   CHECK(metadata_length > 0);
56
57   int index = vector->GetIndex(FeedbackVectorSlot(0));
58
59   CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length, index);
60   CHECK(FeedbackVectorSlot(0) == vector->ToSlot(index));
61
62   index = vector->GetIndex(FeedbackVectorICSlot(0));
63   CHECK_EQ(index,
64            TypeFeedbackVector::kReservedIndexCount + metadata_length + 3);
65   CHECK(FeedbackVectorICSlot(0) == vector->ToICSlot(index));
66   CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length + 3 +
67                5 * TypeFeedbackVector::elements_per_ic_slot(),
68            vector->length());
69 }
70
71
72 // IC slots need an encoding to recognize what is in there.
73 TEST(VectorICMetadata) {
74   LocalContext context;
75   v8::HandleScope scope(context->GetIsolate());
76   Isolate* isolate = CcTest::i_isolate();
77   Factory* factory = isolate->factory();
78   Zone* zone = isolate->runtime_zone();
79
80   ZoneFeedbackVectorSpec spec(zone, 10, 3 * 10);
81   // Set metadata.
82   for (int i = 0; i < 30; i++) {
83     Code::Kind kind;
84     if (i % 3 == 0) {
85       kind = Code::CALL_IC;
86     } else if (i % 3 == 1) {
87       kind = Code::LOAD_IC;
88     } else {
89       kind = Code::KEYED_LOAD_IC;
90     }
91     spec.SetKind(i, kind);
92   }
93
94   Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&spec);
95   CHECK_EQ(10, vector->Slots());
96   CHECK_EQ(3 * 10, vector->ICSlots());
97
98   // Meanwhile set some feedback values and type feedback values to
99   // verify the data structure remains intact.
100   vector->change_ic_with_type_info_count(100);
101   vector->change_ic_generic_count(3333);
102   vector->Set(FeedbackVectorSlot(0), *vector);
103
104   // Verify the metadata is correctly set up from the spec.
105   for (int i = 0; i < 30; i++) {
106     Code::Kind kind = vector->GetKind(FeedbackVectorICSlot(i));
107     if (i % 3 == 0) {
108       CHECK_EQ(Code::CALL_IC, kind);
109     } else if (i % 3 == 1) {
110       CHECK_EQ(Code::LOAD_IC, kind);
111     } else {
112       CHECK_EQ(Code::KEYED_LOAD_IC, kind);
113     }
114   }
115 }
116
117
118 TEST(VectorSlotClearing) {
119   LocalContext context;
120   v8::HandleScope scope(context->GetIsolate());
121   Isolate* isolate = CcTest::i_isolate();
122   Factory* factory = isolate->factory();
123
124   // We only test clearing FeedbackVectorSlots, not FeedbackVectorICSlots.
125   // The reason is that FeedbackVectorICSlots need a full code environment
126   // to fully test (See VectorICProfilerStatistics test below).
127   FeedbackVectorSpec spec(5);
128   Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&spec);
129
130   // Fill with information
131   vector->Set(FeedbackVectorSlot(0), Smi::FromInt(1));
132   Handle<WeakCell> cell = factory->NewWeakCell(factory->fixed_array_map());
133   vector->Set(FeedbackVectorSlot(1), *cell);
134   Handle<AllocationSite> site = factory->NewAllocationSite();
135   vector->Set(FeedbackVectorSlot(2), *site);
136
137   // GC time clearing leaves slots alone.
138   vector->ClearSlotsAtGCTime(NULL);
139   Object* obj = vector->Get(FeedbackVectorSlot(1));
140   CHECK(obj->IsWeakCell() && !WeakCell::cast(obj)->cleared());
141
142   vector->ClearSlots(NULL);
143
144   // The feedback vector slots are cleared. AllocationSites are still granted
145   // an exemption from clearing, as are smis.
146   CHECK_EQ(Smi::FromInt(1), vector->Get(FeedbackVectorSlot(0)));
147   CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),
148            vector->Get(FeedbackVectorSlot(1)));
149   CHECK(vector->Get(FeedbackVectorSlot(2))->IsAllocationSite());
150 }
151
152
153 TEST(VectorICProfilerStatistics) {
154   if (i::FLAG_always_opt) return;
155   CcTest::InitializeVM();
156   LocalContext context;
157   v8::HandleScope scope(context->GetIsolate());
158   Isolate* isolate = CcTest::i_isolate();
159   Heap* heap = isolate->heap();
160
161   // Make sure function f has a call that uses a type feedback slot.
162   CompileRun(
163       "function fun() {};"
164       "function f(a) { a(); } f(fun);");
165   Handle<JSFunction> f = v8::Utils::OpenHandle(
166       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
167   // There should be one IC.
168   Handle<Code> code = handle(f->shared()->code(), isolate);
169   TypeFeedbackInfo* feedback_info =
170       TypeFeedbackInfo::cast(code->type_feedback_info());
171   CHECK_EQ(1, feedback_info->ic_total_count());
172   CHECK_EQ(0, feedback_info->ic_with_type_info_count());
173   CHECK_EQ(0, feedback_info->ic_generic_count());
174   Handle<TypeFeedbackVector> feedback_vector =
175       handle(f->shared()->feedback_vector(), isolate);
176   int ic_slot = 0;
177   CallICNexus nexus(feedback_vector, FeedbackVectorICSlot(ic_slot));
178   CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
179   CHECK_EQ(0, feedback_vector->ic_generic_count());
180
181   // Now send the information generic.
182   CompileRun("f(Object);");
183   CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
184   CHECK_EQ(1, feedback_vector->ic_generic_count());
185
186   // A collection will not affect the site.
187   heap->CollectAllGarbage();
188   CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
189   CHECK_EQ(1, feedback_vector->ic_generic_count());
190
191   // The Array function is special. A call to array remains monomorphic
192   // and isn't cleared by gc because an AllocationSite is being held.
193   // Clear the IC manually in order to test this case.
194   nexus.Clear(*code);
195   CompileRun("f(Array);");
196   CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
197   CHECK_EQ(0, feedback_vector->ic_generic_count());
198
199
200   CHECK(nexus.GetFeedback()->IsAllocationSite());
201   heap->CollectAllGarbage();
202   CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
203   CHECK_EQ(0, feedback_vector->ic_generic_count());
204   CHECK(nexus.GetFeedback()->IsAllocationSite());
205 }
206
207
208 TEST(VectorCallICStates) {
209   if (i::FLAG_always_opt) return;
210   CcTest::InitializeVM();
211   LocalContext context;
212   v8::HandleScope scope(context->GetIsolate());
213   Isolate* isolate = CcTest::i_isolate();
214   Heap* heap = isolate->heap();
215
216   // Make sure function f has a call that uses a type feedback slot.
217   CompileRun(
218       "function foo() { return 17; }"
219       "function f(a) { a(); } f(foo);");
220   Handle<JSFunction> f = v8::Utils::OpenHandle(
221       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
222   // There should be one IC.
223   Handle<TypeFeedbackVector> feedback_vector =
224       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
225   FeedbackVectorICSlot slot(0);
226   CallICNexus nexus(feedback_vector, slot);
227   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
228   // CallIC doesn't return map feedback.
229   CHECK(!nexus.FindFirstMap());
230
231   CompileRun("f(function() { return 16; })");
232   CHECK_EQ(GENERIC, nexus.StateFromFeedback());
233
234   // After a collection, state should remain GENERIC.
235   heap->CollectAllGarbage();
236   CHECK_EQ(GENERIC, nexus.StateFromFeedback());
237
238   // A call to Array is special, it contains an AllocationSite as feedback.
239   // Clear the IC manually in order to test this case.
240   nexus.Clear(f->shared()->code());
241   CompileRun("f(Array)");
242   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
243   CHECK(nexus.GetFeedback()->IsAllocationSite());
244
245   heap->CollectAllGarbage();
246   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
247 }
248
249
250 TEST(VectorLoadICStates) {
251   if (i::FLAG_always_opt) return;
252   CcTest::InitializeVM();
253   LocalContext context;
254   v8::HandleScope scope(context->GetIsolate());
255   Isolate* isolate = CcTest::i_isolate();
256   Heap* heap = isolate->heap();
257
258   // Make sure function f has a call that uses a type feedback slot.
259   CompileRun(
260       "var o = { foo: 3 };"
261       "function f(a) { return a.foo; } f(o);");
262   Handle<JSFunction> f = v8::Utils::OpenHandle(
263       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
264   // There should be one IC.
265   Handle<TypeFeedbackVector> feedback_vector =
266       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
267   FeedbackVectorICSlot slot(0);
268   LoadICNexus nexus(feedback_vector, slot);
269   CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
270
271   CompileRun("f(o)");
272   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
273   // Verify that the monomorphic map is the one we expect.
274   Handle<JSObject> o = v8::Utils::OpenHandle(
275       *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
276   CHECK_EQ(o->map(), nexus.FindFirstMap());
277
278   // Now go polymorphic.
279   CompileRun("f({ blarg: 3, foo: 2 })");
280   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
281
282   CompileRun(
283       "delete o.foo;"
284       "f(o)");
285   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
286
287   CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
288   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
289   MapHandleList maps;
290   nexus.FindAllMaps(&maps);
291   CHECK_EQ(4, maps.length());
292
293   // Finally driven megamorphic.
294   CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
295   CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
296   CHECK(!nexus.FindFirstMap());
297
298   // After a collection, state should not be reset to PREMONOMORPHIC.
299   heap->CollectAllGarbage();
300   CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
301 }
302
303
304 TEST(VectorLoadICSlotSharing) {
305   if (i::FLAG_always_opt) return;
306   CcTest::InitializeVM();
307   LocalContext context;
308   v8::HandleScope scope(context->GetIsolate());
309   Isolate* isolate = CcTest::i_isolate();
310
311   // Function f has 3 LoadICs, one for each o, but the ICs share the same
312   // feedback vector IC slot.
313   CompileRun(
314       "o = 10;"
315       "function f() {"
316       "  var x = o + 10;"
317       "  return o + x + o;"
318       "}"
319       "f();");
320   Handle<JSFunction> f = v8::Utils::OpenHandle(
321       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
322   // There should be one IC slot.
323   Handle<TypeFeedbackVector> feedback_vector =
324       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
325   CHECK_EQ(1, feedback_vector->ICSlots());
326   FeedbackVectorICSlot slot(0);
327   LoadICNexus nexus(feedback_vector, slot);
328   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
329 }
330
331
332 TEST(VectorLoadICOnSmi) {
333   if (i::FLAG_always_opt) return;
334   CcTest::InitializeVM();
335   LocalContext context;
336   v8::HandleScope scope(context->GetIsolate());
337   Isolate* isolate = CcTest::i_isolate();
338   Heap* heap = isolate->heap();
339
340   // Make sure function f has a call that uses a type feedback slot.
341   CompileRun(
342       "var o = { foo: 3 };"
343       "function f(a) { return a.foo; } f(o);");
344   Handle<JSFunction> f = v8::Utils::OpenHandle(
345       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
346   // There should be one IC.
347   Handle<TypeFeedbackVector> feedback_vector =
348       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
349   FeedbackVectorICSlot slot(0);
350   LoadICNexus nexus(feedback_vector, slot);
351   CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
352
353   CompileRun("f(34)");
354   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
355   // Verify that the monomorphic map is the one we expect.
356   Map* number_map = heap->heap_number_map();
357   CHECK_EQ(number_map, nexus.FindFirstMap());
358
359   // Now go polymorphic on o.
360   CompileRun("f(o)");
361   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
362
363   MapHandleList maps;
364   nexus.FindAllMaps(&maps);
365   CHECK_EQ(2, maps.length());
366
367   // One of the maps should be the o map.
368   Handle<JSObject> o = v8::Utils::OpenHandle(
369       *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
370   bool number_map_found = false;
371   bool o_map_found = false;
372   for (int i = 0; i < maps.length(); i++) {
373     Handle<Map> current = maps[i];
374     if (*current == number_map)
375       number_map_found = true;
376     else if (*current == o->map())
377       o_map_found = true;
378   }
379   CHECK(number_map_found && o_map_found);
380
381   // The degree of polymorphism doesn't change.
382   CompileRun("f(100)");
383   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
384   MapHandleList maps2;
385   nexus.FindAllMaps(&maps2);
386   CHECK_EQ(2, maps2.length());
387 }
388
389
390 static Handle<JSFunction> GetFunction(const char* name) {
391   Handle<JSFunction> f = v8::Utils::OpenHandle(
392       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str(name))));
393   return f;
394 }
395
396
397 TEST(ReferenceContextAllocatesNoSlots) {
398   if (i::FLAG_always_opt) return;
399   CcTest::InitializeVM();
400   LocalContext context;
401   v8::HandleScope scope(context->GetIsolate());
402   Isolate* isolate = CcTest::i_isolate();
403
404   CompileRun(
405       "function testvar(x) {"
406       "  y = x;"
407       "  y = a;"
408       "  return y;"
409       "}"
410       "a = 3;"
411       "testvar({});");
412
413   Handle<JSFunction> f = GetFunction("testvar");
414
415   // There should be two LOAD_ICs, one for a and one for y at the end.
416   Handle<TypeFeedbackVector> feedback_vector =
417       handle(f->shared()->feedback_vector(), isolate);
418   CHECK_EQ(2, feedback_vector->ICSlots());
419   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(0)) == Code::LOAD_IC);
420   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(1)) == Code::LOAD_IC);
421
422   CompileRun(
423       "function testprop(x) {"
424       "  x.blue = a;"
425       "}"
426       "testprop({ blue: 3 });");
427
428   f = GetFunction("testprop");
429
430   // There should be one LOAD_IC, for the load of a.
431   feedback_vector = handle(f->shared()->feedback_vector(), isolate);
432   CHECK_EQ(1, feedback_vector->ICSlots());
433
434   CompileRun(
435       "function testpropfunc(x) {"
436       "  x().blue = a;"
437       "  return x().blue;"
438       "}"
439       "function makeresult() { return { blue: 3 }; }"
440       "testpropfunc(makeresult);");
441
442   f = GetFunction("testpropfunc");
443
444   // There should be 2 LOAD_ICs and 2 CALL_ICs.
445   feedback_vector = handle(f->shared()->feedback_vector(), isolate);
446   CHECK_EQ(4, feedback_vector->ICSlots());
447   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(0)) == Code::CALL_IC);
448   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(1)) == Code::LOAD_IC);
449   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(2)) == Code::CALL_IC);
450   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(3)) == Code::LOAD_IC);
451
452   CompileRun(
453       "function testkeyedprop(x) {"
454       "  x[0] = a;"
455       "  return x[0];"
456       "}"
457       "testkeyedprop([0, 1, 2]);");
458
459   f = GetFunction("testkeyedprop");
460
461   // There should be 1 LOAD_ICs for the load of a, and one KEYED_LOAD_IC for the
462   // load of x[0] in the return statement.
463   feedback_vector = handle(f->shared()->feedback_vector(), isolate);
464   CHECK_EQ(2, feedback_vector->ICSlots());
465   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(0)) == Code::LOAD_IC);
466   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(1)) ==
467         Code::KEYED_LOAD_IC);
468
469   CompileRun(
470       "function testcompound(x) {"
471       "  x.old = x.young = x.in_between = a;"
472       "  return x.old + x.young;"
473       "}"
474       "testcompound({ old: 3, young: 3, in_between: 3 });");
475
476   f = GetFunction("testcompound");
477
478   // There should be 3 LOAD_ICs, for load of a and load of x.old and x.young.
479   feedback_vector = handle(f->shared()->feedback_vector(), isolate);
480   CHECK_EQ(3, feedback_vector->ICSlots());
481   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(0)) == Code::LOAD_IC);
482   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(1)) == Code::LOAD_IC);
483   CHECK(feedback_vector->GetKind(FeedbackVectorICSlot(2)) == Code::LOAD_IC);
484 }
485 }