v8:3539 - hold constructor feedback in weak cells
[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   if (FLAG_vector_ics) {
50     for (int i = 0; i < 5; i++) spec.SetKind(i, Code::CALL_IC);
51   }
52   vector = factory->NewTypeFeedbackVector(&spec);
53   CHECK_EQ(3, vector->Slots());
54   CHECK_EQ(5, vector->ICSlots());
55
56   int metadata_length = vector->ic_metadata_length();
57   if (!FLAG_vector_ics) {
58     CHECK_EQ(0, metadata_length);
59   } else {
60     CHECK(metadata_length > 0);
61   }
62
63   int index = vector->GetIndex(FeedbackVectorSlot(0));
64
65   CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length, index);
66   CHECK(FeedbackVectorSlot(0) == vector->ToSlot(index));
67
68   index = vector->GetIndex(FeedbackVectorICSlot(0));
69   CHECK_EQ(index,
70            TypeFeedbackVector::kReservedIndexCount + metadata_length + 3);
71   CHECK(FeedbackVectorICSlot(0) == vector->ToICSlot(index));
72   CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length + 3 +
73                5 * TypeFeedbackVector::elements_per_ic_slot(),
74            vector->length());
75 }
76
77
78 // IC slots need an encoding to recognize what is in there.
79 TEST(VectorICMetadata) {
80   LocalContext context;
81   v8::HandleScope scope(context->GetIsolate());
82   if (!FLAG_vector_ics) {
83     // If FLAG_vector_ics is false, we only store CALL_ICs in the vector, so
84     // there is no need for metadata to describe the slots.
85     return;
86   }
87   Isolate* isolate = CcTest::i_isolate();
88   Factory* factory = isolate->factory();
89   Zone* zone = isolate->runtime_zone();
90
91   ZoneFeedbackVectorSpec spec(zone, 10, 3 * 10);
92   // Set metadata.
93   for (int i = 0; i < 30; i++) {
94     Code::Kind kind;
95     if (i % 3 == 0) {
96       kind = Code::CALL_IC;
97     } else if (i % 3 == 1) {
98       kind = Code::LOAD_IC;
99     } else {
100       kind = Code::KEYED_LOAD_IC;
101     }
102     spec.SetKind(i, kind);
103   }
104
105   Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&spec);
106   CHECK_EQ(10, vector->Slots());
107   CHECK_EQ(3 * 10, vector->ICSlots());
108
109   // Meanwhile set some feedback values and type feedback values to
110   // verify the data structure remains intact.
111   vector->change_ic_with_type_info_count(100);
112   vector->change_ic_generic_count(3333);
113   vector->Set(FeedbackVectorSlot(0), *vector);
114
115   // Verify the metadata is correctly set up from the spec.
116   for (int i = 0; i < 30; i++) {
117     Code::Kind kind = vector->GetKind(FeedbackVectorICSlot(i));
118     if (i % 3 == 0) {
119       CHECK_EQ(Code::CALL_IC, kind);
120     } else if (i % 3 == 1) {
121       CHECK_EQ(Code::LOAD_IC, kind);
122     } else {
123       CHECK_EQ(Code::KEYED_LOAD_IC, kind);
124     }
125   }
126 }
127
128
129 TEST(VectorSlotClearing) {
130   LocalContext context;
131   v8::HandleScope scope(context->GetIsolate());
132   Isolate* isolate = CcTest::i_isolate();
133   Factory* factory = isolate->factory();
134
135   // We only test clearing FeedbackVectorSlots, not FeedbackVectorICSlots.
136   // The reason is that FeedbackVectorICSlots need a full code environment
137   // to fully test (See VectorICProfilerStatistics test below).
138   FeedbackVectorSpec spec(5);
139   Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&spec);
140
141   // Fill with information
142   vector->Set(FeedbackVectorSlot(0), Smi::FromInt(1));
143   Handle<WeakCell> cell = factory->NewWeakCell(factory->fixed_array_map());
144   vector->Set(FeedbackVectorSlot(1), *cell);
145   Handle<AllocationSite> site = factory->NewAllocationSite();
146   vector->Set(FeedbackVectorSlot(2), *site);
147
148   // GC time clearing leaves slots alone.
149   vector->ClearSlotsAtGCTime(NULL);
150   Object* obj = vector->Get(FeedbackVectorSlot(1));
151   CHECK(obj->IsWeakCell() && !WeakCell::cast(obj)->cleared());
152
153   vector->ClearSlots(NULL);
154
155   // The feedback vector slots are cleared. AllocationSites are still granted
156   // an exemption from clearing, as are smis.
157   CHECK_EQ(Smi::FromInt(1), vector->Get(FeedbackVectorSlot(0)));
158   CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),
159            vector->Get(FeedbackVectorSlot(1)));
160   CHECK(vector->Get(FeedbackVectorSlot(2))->IsAllocationSite());
161 }
162
163
164 TEST(VectorICProfilerStatistics) {
165   if (i::FLAG_always_opt) return;
166   CcTest::InitializeVM();
167   LocalContext context;
168   v8::HandleScope scope(context->GetIsolate());
169   Isolate* isolate = CcTest::i_isolate();
170   Heap* heap = isolate->heap();
171
172   // Make sure function f has a call that uses a type feedback slot.
173   CompileRun(
174       "function fun() {};"
175       "function f(a) { a(); } f(fun);");
176   Handle<JSFunction> f = v8::Utils::OpenHandle(
177       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
178   // There should be one IC.
179   Handle<Code> code = handle(f->shared()->code(), isolate);
180   TypeFeedbackInfo* feedback_info =
181       TypeFeedbackInfo::cast(code->type_feedback_info());
182   CHECK_EQ(1, feedback_info->ic_total_count());
183   CHECK_EQ(0, feedback_info->ic_with_type_info_count());
184   CHECK_EQ(0, feedback_info->ic_generic_count());
185   Handle<TypeFeedbackVector> feedback_vector =
186       handle(f->shared()->feedback_vector(), isolate);
187   int ic_slot = 0;
188   CallICNexus nexus(feedback_vector, FeedbackVectorICSlot(ic_slot));
189   CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
190   CHECK_EQ(0, feedback_vector->ic_generic_count());
191
192   // Now send the information generic.
193   CompileRun("f(Object);");
194   CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
195   CHECK_EQ(1, feedback_vector->ic_generic_count());
196
197   // A collection will not affect the site.
198   heap->CollectAllGarbage(i::Heap::kNoGCFlags);
199   CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
200   CHECK_EQ(1, feedback_vector->ic_generic_count());
201
202   // The Array function is special. A call to array remains monomorphic
203   // and isn't cleared by gc because an AllocationSite is being held.
204   // Clear the IC manually in order to test this case.
205   nexus.Clear(*code);
206   CompileRun("f(Array);");
207   CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
208   CHECK_EQ(0, feedback_vector->ic_generic_count());
209
210
211   CHECK(nexus.GetFeedback()->IsAllocationSite());
212   heap->CollectAllGarbage(i::Heap::kNoGCFlags);
213   CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
214   CHECK_EQ(0, feedback_vector->ic_generic_count());
215   CHECK(nexus.GetFeedback()->IsAllocationSite());
216 }
217
218
219 TEST(VectorCallICStates) {
220   if (i::FLAG_always_opt) return;
221   CcTest::InitializeVM();
222   LocalContext context;
223   v8::HandleScope scope(context->GetIsolate());
224   Isolate* isolate = CcTest::i_isolate();
225   Heap* heap = isolate->heap();
226
227   // Make sure function f has a call that uses a type feedback slot.
228   CompileRun(
229       "function foo() { return 17; }"
230       "function f(a) { a(); } f(foo);");
231   Handle<JSFunction> f = v8::Utils::OpenHandle(
232       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
233   // There should be one IC.
234   Handle<TypeFeedbackVector> feedback_vector =
235       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
236   FeedbackVectorICSlot slot(0);
237   CallICNexus nexus(feedback_vector, slot);
238   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
239   // CallIC doesn't return map feedback.
240   CHECK(!nexus.FindFirstMap());
241
242   CompileRun("f(function() { return 16; })");
243   CHECK_EQ(GENERIC, nexus.StateFromFeedback());
244
245   // After a collection, state should remain GENERIC.
246   heap->CollectAllGarbage(i::Heap::kNoGCFlags);
247   CHECK_EQ(GENERIC, nexus.StateFromFeedback());
248
249   // A call to Array is special, it contains an AllocationSite as feedback.
250   // Clear the IC manually in order to test this case.
251   nexus.Clear(f->shared()->code());
252   CompileRun("f(Array)");
253   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
254   CHECK(nexus.GetFeedback()->IsAllocationSite());
255
256   heap->CollectAllGarbage(i::Heap::kNoGCFlags);
257   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
258 }
259
260
261 TEST(VectorLoadICStates) {
262   if (i::FLAG_always_opt || !i::FLAG_vector_ics) return;
263   CcTest::InitializeVM();
264   LocalContext context;
265   v8::HandleScope scope(context->GetIsolate());
266   Isolate* isolate = CcTest::i_isolate();
267   Heap* heap = isolate->heap();
268
269   // Make sure function f has a call that uses a type feedback slot.
270   CompileRun(
271       "var o = { foo: 3 };"
272       "function f(a) { return a.foo; } f(o);");
273   Handle<JSFunction> f = v8::Utils::OpenHandle(
274       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
275   // There should be one IC.
276   Handle<TypeFeedbackVector> feedback_vector =
277       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
278   FeedbackVectorICSlot slot(0);
279   LoadICNexus nexus(feedback_vector, slot);
280   CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
281
282   CompileRun("f(o)");
283   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
284   // Verify that the monomorphic map is the one we expect.
285   Handle<JSObject> o = v8::Utils::OpenHandle(
286       *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
287   CHECK_EQ(o->map(), nexus.FindFirstMap());
288
289   // Now go polymorphic.
290   CompileRun("f({ blarg: 3, foo: 2 })");
291   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
292
293   CompileRun(
294       "delete o.foo;"
295       "f(o)");
296   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
297
298   CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
299   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
300   MapHandleList maps;
301   nexus.FindAllMaps(&maps);
302   CHECK_EQ(4, maps.length());
303
304   // Finally driven megamorphic.
305   CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
306   CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
307   CHECK(!nexus.FindFirstMap());
308
309   // After a collection, state should not be reset to PREMONOMORPHIC.
310   heap->CollectAllGarbage(i::Heap::kNoGCFlags);
311   CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
312 }
313
314
315 TEST(VectorLoadICSlotSharing) {
316   if (i::FLAG_always_opt || !i::FLAG_vector_ics) return;
317   CcTest::InitializeVM();
318   LocalContext context;
319   v8::HandleScope scope(context->GetIsolate());
320   Isolate* isolate = CcTest::i_isolate();
321
322   // Function f has 3 LoadICs, one for each o, but the ICs share the same
323   // feedback vector IC slot.
324   CompileRun(
325       "var o = 10;"
326       "function f() {"
327       "  var x = o + 10;"
328       "  return o + x + o;"
329       "}"
330       "f();");
331   Handle<JSFunction> f = v8::Utils::OpenHandle(
332       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
333   // There should be one IC slot.
334   Handle<TypeFeedbackVector> feedback_vector =
335       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
336   CHECK_EQ(1, feedback_vector->ICSlots());
337   FeedbackVectorICSlot slot(0);
338   LoadICNexus nexus(feedback_vector, slot);
339   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
340 }
341
342
343 TEST(VectorLoadICOnSmi) {
344   if (i::FLAG_always_opt || !i::FLAG_vector_ics) return;
345   CcTest::InitializeVM();
346   LocalContext context;
347   v8::HandleScope scope(context->GetIsolate());
348   Isolate* isolate = CcTest::i_isolate();
349   Heap* heap = isolate->heap();
350
351   // Make sure function f has a call that uses a type feedback slot.
352   CompileRun(
353       "var o = { foo: 3 };"
354       "function f(a) { return a.foo; } f(o);");
355   Handle<JSFunction> f = v8::Utils::OpenHandle(
356       *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
357   // There should be one IC.
358   Handle<TypeFeedbackVector> feedback_vector =
359       Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
360   FeedbackVectorICSlot slot(0);
361   LoadICNexus nexus(feedback_vector, slot);
362   CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
363
364   CompileRun("f(34)");
365   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
366   // Verify that the monomorphic map is the one we expect.
367   Map* number_map = heap->heap_number_map();
368   CHECK_EQ(number_map, nexus.FindFirstMap());
369
370   // Now go polymorphic on o.
371   CompileRun("f(o)");
372   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
373
374   MapHandleList maps;
375   nexus.FindAllMaps(&maps);
376   CHECK_EQ(2, maps.length());
377
378   // One of the maps should be the o map.
379   Handle<JSObject> o = v8::Utils::OpenHandle(
380       *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
381   bool number_map_found = false;
382   bool o_map_found = false;
383   for (int i = 0; i < maps.length(); i++) {
384     Handle<Map> current = maps[i];
385     if (*current == number_map)
386       number_map_found = true;
387     else if (*current == o->map())
388       o_map_found = true;
389   }
390   CHECK(number_map_found && o_map_found);
391
392   // The degree of polymorphism doesn't change.
393   CompileRun("f(100)");
394   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
395   MapHandleList maps2;
396   nexus.FindAllMaps(&maps2);
397   CHECK_EQ(2, maps2.length());
398 }
399 }