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
17using namespace v8::internal;
18
19namespace {
20
21#define CHECK_SLOT_KIND(helper, index, expected_kind) \
22  CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index)));
23
24
25static 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
34TEST(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.
102TEST(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
156TEST(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
196TEST(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
236TEST(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
272TEST(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
327TEST(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
355TEST(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
414TEST(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
529TEST(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