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