• 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->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 
TEST(VectorCallCounts)236 TEST(VectorCallCounts) {
237   if (i::FLAG_always_opt) return;
238   CcTest::InitializeVM();
239   LocalContext context;
240   v8::HandleScope scope(context->GetIsolate());
241   Isolate* isolate = CcTest::i_isolate();
242 
243   // Make sure function f has a call that uses a type feedback slot.
244   CompileRun(
245       "function foo() { return 17; }"
246       "function f(a) { a(); } f(foo);");
247   Handle<JSFunction> f = GetFunction("f");
248   // There should be one IC.
249   Handle<TypeFeedbackVector> feedback_vector =
250       Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
251   FeedbackVectorSlot slot(0);
252   CallICNexus nexus(feedback_vector, slot);
253   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
254 
255   CompileRun("f(foo); f(foo);");
256   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
257   CHECK_EQ(3, nexus.ExtractCallCount());
258 
259   CompileRun(
260       "function Foo() {}"
261       "function f(a) { new a(); } f(Foo);");
262   f = GetFunction("f");
263   // There should be one IC.
264   feedback_vector = Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
265   FeedbackVectorSlot cslot(1);
266 
267   CompileRun("f(Foo); f(Foo);");
268   CHECK(feedback_vector->Get(cslot)->IsSmi());
269   CHECK_EQ(3, Smi::cast(feedback_vector->Get(cslot))->value());
270 }
271 
TEST(VectorLoadICStates)272 TEST(VectorLoadICStates) {
273   if (i::FLAG_always_opt) return;
274   CcTest::InitializeVM();
275   LocalContext context;
276   v8::HandleScope scope(context->GetIsolate());
277   Isolate* isolate = CcTest::i_isolate();
278   Heap* heap = isolate->heap();
279 
280   // Make sure function f has a call that uses a type feedback slot.
281   CompileRun(
282       "var o = { foo: 3 };"
283       "function f(a) { return a.foo; } f(o);");
284   Handle<JSFunction> f = GetFunction("f");
285   // There should be one IC.
286   Handle<TypeFeedbackVector> feedback_vector =
287       Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
288   FeedbackVectorSlot slot(0);
289   LoadICNexus nexus(feedback_vector, slot);
290   CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
291 
292   CompileRun("f(o)");
293   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
294   // Verify that the monomorphic map is the one we expect.
295   v8::MaybeLocal<v8::Value> v8_o =
296       CcTest::global()->Get(context.local(), v8_str("o"));
297   Handle<JSObject> o =
298       Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
299   CHECK_EQ(o->map(), nexus.FindFirstMap());
300 
301   // Now go polymorphic.
302   CompileRun("f({ blarg: 3, foo: 2 })");
303   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
304 
305   CompileRun(
306       "delete o.foo;"
307       "f(o)");
308   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
309 
310   CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
311   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
312   MapHandleList maps;
313   nexus.FindAllMaps(&maps);
314   CHECK_EQ(4, maps.length());
315 
316   // Finally driven megamorphic.
317   CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
318   CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
319   CHECK(!nexus.FindFirstMap());
320 
321   // After a collection, state should not be reset to PREMONOMORPHIC.
322   heap->CollectAllGarbage();
323   CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
324 }
325 
326 
TEST(VectorLoadICSlotSharing)327 TEST(VectorLoadICSlotSharing) {
328   if (i::FLAG_always_opt) return;
329   CcTest::InitializeVM();
330   LocalContext context;
331   v8::HandleScope scope(context->GetIsolate());
332   Isolate* isolate = CcTest::i_isolate();
333 
334   // Function f has 3 LoadICs, one for each o, but the ICs share the same
335   // feedback vector IC slot.
336   CompileRun(
337       "o = 10;"
338       "function f() {"
339       "  var x = o + 10;"
340       "  return o + x + o;"
341       "}"
342       "f();");
343   Handle<JSFunction> f = GetFunction("f");
344   // There should be one IC slot.
345   Handle<TypeFeedbackVector> feedback_vector =
346       Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
347   FeedbackVectorHelper helper(feedback_vector);
348   CHECK_EQ(1, helper.slot_count());
349   FeedbackVectorSlot slot(0);
350   LoadGlobalICNexus nexus(feedback_vector, slot);
351   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
352 }
353 
354 
TEST(VectorLoadICOnSmi)355 TEST(VectorLoadICOnSmi) {
356   if (i::FLAG_always_opt) return;
357   CcTest::InitializeVM();
358   LocalContext context;
359   v8::HandleScope scope(context->GetIsolate());
360   Isolate* isolate = CcTest::i_isolate();
361   Heap* heap = isolate->heap();
362 
363   // Make sure function f has a call that uses a type feedback slot.
364   CompileRun(
365       "var o = { foo: 3 };"
366       "function f(a) { return a.foo; } f(o);");
367   Handle<JSFunction> f = GetFunction("f");
368   // There should be one IC.
369   Handle<TypeFeedbackVector> feedback_vector =
370       Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
371   FeedbackVectorSlot slot(0);
372   LoadICNexus nexus(feedback_vector, slot);
373   CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
374 
375   CompileRun("f(34)");
376   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
377   // Verify that the monomorphic map is the one we expect.
378   Map* number_map = heap->heap_number_map();
379   CHECK_EQ(number_map, nexus.FindFirstMap());
380 
381   // Now go polymorphic on o.
382   CompileRun("f(o)");
383   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
384 
385   MapHandleList maps;
386   nexus.FindAllMaps(&maps);
387   CHECK_EQ(2, maps.length());
388 
389   // One of the maps should be the o map.
390   v8::MaybeLocal<v8::Value> v8_o =
391       CcTest::global()->Get(context.local(), v8_str("o"));
392   Handle<JSObject> o =
393       Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
394   bool number_map_found = false;
395   bool o_map_found = false;
396   for (int i = 0; i < maps.length(); i++) {
397     Handle<Map> current = maps[i];
398     if (*current == number_map)
399       number_map_found = true;
400     else if (*current == o->map())
401       o_map_found = true;
402   }
403   CHECK(number_map_found && o_map_found);
404 
405   // The degree of polymorphism doesn't change.
406   CompileRun("f(100)");
407   CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
408   MapHandleList maps2;
409   nexus.FindAllMaps(&maps2);
410   CHECK_EQ(2, maps2.length());
411 }
412 
413 
TEST(ReferenceContextAllocatesNoSlots)414 TEST(ReferenceContextAllocatesNoSlots) {
415   if (i::FLAG_always_opt) return;
416   CcTest::InitializeVM();
417   LocalContext context;
418   v8::HandleScope scope(context->GetIsolate());
419   Isolate* isolate = CcTest::i_isolate();
420 
421   {
422     CompileRun(
423         "function testvar(x) {"
424         "  y = x;"
425         "  y = a;"
426         "  return y;"
427         "}"
428         "a = 3;"
429         "testvar({});");
430 
431     Handle<JSFunction> f = GetFunction("testvar");
432 
433     // There should be two LOAD_ICs, one for a and one for y at the end.
434     Handle<TypeFeedbackVector> feedback_vector =
435         handle(f->feedback_vector(), isolate);
436     FeedbackVectorHelper helper(feedback_vector);
437     CHECK_EQ(4, helper.slot_count());
438     CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::STORE_IC);
439     CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
440     CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
441     CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
442   }
443 
444   {
445     CompileRun(
446         "function testprop(x) {"
447         "  x.blue = a;"
448         "}"
449         "testprop({ blue: 3 });");
450 
451     Handle<JSFunction> f = GetFunction("testprop");
452 
453     // There should be one LOAD_IC, for the load of a.
454     Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
455     FeedbackVectorHelper helper(feedback_vector);
456     CHECK_EQ(2, helper.slot_count());
457     CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
458     CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::STORE_IC);
459   }
460 
461   {
462     CompileRun(
463         "function testpropfunc(x) {"
464         "  x().blue = a;"
465         "  return x().blue;"
466         "}"
467         "function makeresult() { return { blue: 3 }; }"
468         "testpropfunc(makeresult);");
469 
470     Handle<JSFunction> f = GetFunction("testpropfunc");
471 
472     // There should be 1 LOAD_GLOBAL_IC to load x (in both cases), 2 CALL_ICs
473     // to call x and a LOAD_IC to load blue.
474     Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
475     FeedbackVectorHelper helper(feedback_vector);
476     CHECK_EQ(5, helper.slot_count());
477     CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::CALL_IC);
478     CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
479     CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
480     CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::CALL_IC);
481     CHECK_SLOT_KIND(helper, 4, FeedbackVectorSlotKind::LOAD_IC);
482   }
483 
484   {
485     CompileRun(
486         "function testkeyedprop(x) {"
487         "  x[0] = a;"
488         "  return x[0];"
489         "}"
490         "testkeyedprop([0, 1, 2]);");
491 
492     Handle<JSFunction> f = GetFunction("testkeyedprop");
493 
494     // There should be 1 LOAD_GLOBAL_ICs for the load of a, and one
495     // KEYED_LOAD_IC for the load of x[0] in the return statement.
496     Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
497     FeedbackVectorHelper helper(feedback_vector);
498     CHECK_EQ(3, helper.slot_count());
499     CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
500     CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::KEYED_STORE_IC);
501     CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::KEYED_LOAD_IC);
502   }
503 
504   {
505     CompileRun(
506         "function testcompound(x) {"
507         "  x.old = x.young = x.in_between = a;"
508         "  return x.old + x.young;"
509         "}"
510         "testcompound({ old: 3, young: 3, in_between: 3 });");
511 
512     Handle<JSFunction> f = GetFunction("testcompound");
513 
514     // There should be 1 LOAD_GLOBAL_IC for load of a and 2 LOAD_ICs, for load
515     // of x.old and x.young.
516     Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
517     FeedbackVectorHelper helper(feedback_vector);
518     CHECK_EQ(6, helper.slot_count());
519     CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
520     CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::STORE_IC);
521     CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
522     CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::STORE_IC);
523     CHECK_SLOT_KIND(helper, 4, FeedbackVectorSlotKind::LOAD_IC);
524     CHECK_SLOT_KIND(helper, 5, FeedbackVectorSlotKind::LOAD_IC);
525   }
526 }
527 
528 
TEST(VectorStoreICBasic)529 TEST(VectorStoreICBasic) {
530   if (i::FLAG_always_opt) return;
531 
532   CcTest::InitializeVM();
533   LocalContext context;
534   v8::HandleScope scope(context->GetIsolate());
535 
536   CompileRun(
537       "function f(a) {"
538       "  a.foo = 5;"
539       "}"
540       "var a = { foo: 3 };"
541       "f(a);"
542       "f(a);"
543       "f(a);");
544   Handle<JSFunction> f = GetFunction("f");
545   // There should be one IC slot.
546   Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
547   FeedbackVectorHelper helper(feedback_vector);
548   CHECK_EQ(1, helper.slot_count());
549   FeedbackVectorSlot slot(0);
550   StoreICNexus nexus(feedback_vector, slot);
551   CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
552 }
553 
554 }  // namespace
555