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