test-feedback-vector.cc 25.6 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/init/v8.h"
6 7
#include "test/cctest/cctest.h"

8
#include "src/api/api-inl.h"
9
#include "src/codegen/macro-assembler.h"
10
#include "src/debug/debug.h"
11
#include "src/execution/execution.h"
12
#include "src/handles/global-handles.h"
13
#include "src/heap/factory.h"
14
#include "src/objects/feedback-cell-inl.h"
15
#include "src/objects/objects-inl.h"
16
#include "test/cctest/test-feedback-vector.h"
17

18 19
namespace v8 {
namespace internal {
20 21 22

namespace {

23 24
#define CHECK_SLOT_KIND(helper, index, expected_kind) \
  CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index)));
25 26


27 28
static Handle<JSFunction> GetFunction(const char* name) {
  v8::MaybeLocal<v8::Value> v8_f = CcTest::global()->Get(
29
      CcTest::isolate()->GetCurrentContext(), v8_str(name));
30 31 32 33 34 35
  Handle<JSFunction> f =
      Handle<JSFunction>::cast(v8::Utils::OpenHandle(*v8_f.ToLocalChecked()));
  return f;
}


36 37 38 39 40
TEST(VectorStructure) {
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
41
  Zone zone(isolate->allocator(), ZONE_NAME);
42

43
  Handle<FeedbackVector> vector;
44 45

  {
46
    FeedbackVectorSpec one_slot(&zone);
47
    one_slot.AddForInSlot();
48
    vector = NewFeedbackVector(isolate, &one_slot);
49 50 51
    FeedbackVectorHelper helper(vector);
    CHECK_EQ(1, helper.slot_count());
  }
52

53
  {
54
    FeedbackVectorSpec one_icslot(&zone);
55
    one_icslot.AddCallICSlot();
56
    vector = NewFeedbackVector(isolate, &one_icslot);
57 58 59
    FeedbackVectorHelper helper(vector);
    CHECK_EQ(1, helper.slot_count());
  }
60

61
  {
62
    FeedbackVectorSpec spec(&zone);
63
    for (int i = 0; i < 3; i++) {
64
      spec.AddForInSlot();
65 66 67 68
    }
    for (int i = 0; i < 5; i++) {
      spec.AddCallICSlot();
    }
69
    vector = NewFeedbackVector(isolate, &spec);
70 71 72 73 74
    FeedbackVectorHelper helper(vector);
    CHECK_EQ(8, helper.slot_count());

    int index = vector->GetIndex(helper.slot(0));

75
    CHECK_EQ(helper.slot(0), vector->ToSlot(index));
76 77

    index = vector->GetIndex(helper.slot(3));
78
    CHECK_EQ(helper.slot(3), vector->ToSlot(index));
79 80

    index = vector->GetIndex(helper.slot(7));
81
    CHECK_EQ(3 + 4 * FeedbackMetadata::GetSlotSize(FeedbackSlotKind::kCall),
82
             index);
83
    CHECK_EQ(helper.slot(7), vector->ToSlot(index));
84

85
    CHECK_EQ(3 + 5 * FeedbackMetadata::GetSlotSize(FeedbackSlotKind::kCall),
86
             vector->length());
87
  }
88 89 90

  {
    FeedbackVectorSpec spec(&zone);
91
    spec.AddForInSlot();
92
    spec.AddCreateClosureSlot();
93
    spec.AddForInSlot();
94
    vector = NewFeedbackVector(isolate, &spec);
95
    FeedbackVectorHelper helper(vector);
96
    FeedbackCell cell = *vector->GetClosureFeedbackCell(0);
97
    CHECK_EQ(cell.value(), *factory->undefined_value());
98
  }
99 100 101 102 103 104 105 106
}


// IC slots need an encoding to recognize what is in there.
TEST(VectorICMetadata) {
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
107
  Zone zone(isolate->allocator(), ZONE_NAME);
108

109
  FeedbackVectorSpec spec(&zone);
110
  // Set metadata.
111 112
  for (int i = 0; i < 40; i++) {
    switch (i % 4) {
113
      case 0:
114
        spec.AddForInSlot();
115 116
        break;
      case 1:
117
        spec.AddCallICSlot();
118 119
        break;
      case 2:
120 121 122 123
        spec.AddLoadICSlot();
        break;
      case 3:
        spec.AddKeyedLoadICSlot();
124
        break;
125
    }
126 127
  }

128
  Handle<FeedbackVector> vector = NewFeedbackVector(isolate, &spec);
129 130
  FeedbackVectorHelper helper(vector);
  CHECK_EQ(40, helper.slot_count());
131

132 133
  // Meanwhile set some feedback values and type feedback values to
  // verify the data structure remains intact.
134
  vector->SynchronizedSet(FeedbackSlot(0), MaybeObject::FromObject(*vector));
135

136
  // Verify the metadata is correctly set up from the spec.
137
  for (int i = 0; i < 40; i++) {
138
    FeedbackSlotKind kind = vector->GetKind(helper.slot(i));
139 140
    switch (i % 4) {
      case 0:
141
        CHECK_EQ(FeedbackSlotKind::kForIn, kind);
142 143
        break;
      case 1:
144
        CHECK_EQ(FeedbackSlotKind::kCall, kind);
145 146
        break;
      case 2:
147
        CHECK_EQ(FeedbackSlotKind::kLoadProperty, kind);
148 149
        break;
      case 3:
150
        CHECK_EQ(FeedbackSlotKind::kLoadKeyed, kind);
151
        break;
152 153
    }
  }
154 155 156
}


157
TEST(VectorCallICStates) {
158
  if (!i::FLAG_use_ic) return;
159
  if (i::FLAG_always_opt) return;
160
  FLAG_allow_natives_syntax = true;
161

162 163 164 165 166 167
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  // Make sure function f has a call that uses a type feedback slot.
  CompileRun(
168 169
      "function foo() { return 17; };"
      "%EnsureFeedbackVectorForFunction(f);"
170
      "function f(a) { a(); } f(foo);");
171
  Handle<JSFunction> f = GetFunction("f");
172
  // There should be one IC.
173 174
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
175
  FeedbackSlot slot(0);
176
  FeedbackNexus nexus(feedback_vector, slot);
177
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
178 179

  CompileRun("f(function() { return 16; })");
180
  CHECK_EQ(GENERIC, nexus.ic_state());
181

182
  // After a collection, state should remain GENERIC.
183
  CcTest::CollectAllGarbage();
184
  CHECK_EQ(GENERIC, nexus.ic_state());
185 186
}

187
TEST(VectorCallFeedback) {
188
  if (!i::FLAG_use_ic) return;
189
  if (i::FLAG_always_opt) return;
190
  FLAG_allow_natives_syntax = true;
191

192 193 194 195 196 197 198
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  // Make sure function f has a call that uses a type feedback slot.
  CompileRun(
      "function foo() { return 17; }"
199
      "%EnsureFeedbackVectorForFunction(f);"
200 201 202 203 204 205 206
      "function f(a) { a(); } f(foo);");
  Handle<JSFunction> f = GetFunction("f");
  Handle<JSFunction> foo = GetFunction("foo");
  // There should be one IC.
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
  FeedbackSlot slot(0);
207
  FeedbackNexus nexus(feedback_vector, slot);
208

209
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
210
  HeapObject heap_object;
211
  CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
212
  CHECK_EQ(*foo, heap_object);
213 214 215

  CcTest::CollectAllGarbage();
  // It should stay monomorphic even after a GC.
216
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
217 218
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
TEST(VectorPolymorphicCallFeedback) {
  if (!i::FLAG_use_ic) return;
  if (i::FLAG_always_opt) return;
  FLAG_allow_natives_syntax = true;
  FLAG_lazy_feedback_allocation = false;

  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  // Make sure the call feedback of a() in f() becomes polymorphic.
  CompileRun(
      "function foo_maker() { return () => { return 17; } }"
      "a_foo = foo_maker();"
      "function f(a) { a(); } f(foo_maker());"
      "f(foo_maker());");
  Handle<JSFunction> f = GetFunction("f");
  Handle<JSFunction> a_foo = GetFunction("a_foo");
  // There should be one IC.
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
  FeedbackSlot slot(0);
  FeedbackNexus nexus(feedback_vector, slot);

  CHECK_EQ(POLYMORPHIC, nexus.ic_state());
  HeapObject heap_object;
  CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
  CHECK(heap_object.IsFeedbackCell(isolate));
  // Ensure this is the feedback cell for the closure returned by
  // foo_maker.
  CHECK_EQ(heap_object, a_foo->raw_feedback_cell());
}

252
TEST(VectorCallFeedbackForArray) {
253
  if (!i::FLAG_use_ic) return;
254
  if (i::FLAG_always_opt) return;
255
  FLAG_allow_natives_syntax = true;
256

257 258 259 260 261
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  // Make sure function f has a call that uses a type feedback slot.
262 263 264 265
  CompileRun(
      "function f(a) { a(); };"
      "%EnsureFeedbackVectorForFunction(f);"
      "f(Array);");
266 267
  Handle<JSFunction> f = GetFunction("f");
  // There should be one IC.
268 269
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
270
  FeedbackSlot slot(0);
271
  FeedbackNexus nexus(feedback_vector, slot);
272

273
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
274
  HeapObject heap_object;
275
  CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
276
  CHECK_EQ(*isolate->array_function(), heap_object);
277

278
  CcTest::CollectAllGarbage();
279
  // It should stay monomorphic even after a GC.
280
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
281
}
282

283 284 285 286 287 288 289 290 291 292 293
size_t GetFeedbackVectorLength(Isolate* isolate, const char* src,
                               bool with_oneshot_opt) {
  i::FLAG_enable_one_shot_optimization = with_oneshot_opt;
  i::Handle<i::Object> i_object = v8::Utils::OpenHandle(*CompileRun(src));
  i::Handle<i::JSFunction> f = i::Handle<i::JSFunction>::cast(i_object);
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
  return feedback_vector->length();
}

TEST(OneShotCallICSlotCount) {
294
  if (!i::FLAG_use_ic) return;
295
  if (i::FLAG_always_opt) return;
296 297
  if (i::FLAG_lazy_feedback_allocation) return;
  FLAG_allow_natives_syntax = true;
298

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  i::FLAG_compilation_cache = false;

  const char* no_call = R"(
    function f1() {};
    function f2() {};
    (function() {
      return arguments.callee;
    })();
  )";
  // len = 2 * 1 ldaNamed property
  CHECK_EQ(GetFeedbackVectorLength(isolate, no_call, false), 2);
  // no slots of named property loads/stores in one shot
  CHECK_EQ(GetFeedbackVectorLength(isolate, no_call, true), 0);

  const char* single_call = R"(
    function f1() {};
    function f2() {};
    (function() {
      f1();
      return arguments.callee;
    })();
  )";
  // len = 2 * 1 ldaNamed Slot + 2 * 1 CachedGlobalSlot + 2 * 1 CallICSlot
  CHECK_EQ(GetFeedbackVectorLength(isolate, single_call, false), 6);
  // len = 2 * 1 CachedGlobalSlot
  CHECK_EQ(GetFeedbackVectorLength(isolate, single_call, true), 2);

  const char* multiple_calls = R"(
    function f1() {};
    function f2() {};
    (function() {
      f1();
      f2();
      f1();
      f2();
      return arguments.callee;
    })();
  )";
  // len = 2 * 1 ldaNamedSlot + 2 *  2 CachedGlobalSlot (one for each unique
  // function) + 2 * 4 CallICSlot (one for each function call)
  CHECK_EQ(GetFeedbackVectorLength(isolate, multiple_calls, false), 14);
  // CachedGlobalSlot (one for each unique function)
  // len = 2 * 2 CachedGlobalSlot (one for each unique function)
  CHECK_EQ(GetFeedbackVectorLength(isolate, multiple_calls, true), 4);
}

349
TEST(VectorCallCounts) {
350
  if (!i::FLAG_use_ic) return;
351
  if (i::FLAG_always_opt) return;
352
  FLAG_allow_natives_syntax = true;
353

354 355 356 357 358 359 360 361
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();

  // Make sure function f has a call that uses a type feedback slot.
  CompileRun(
      "function foo() { return 17; }"
362
      "%EnsureFeedbackVectorForFunction(f);"
363 364 365
      "function f(a) { a(); } f(foo);");
  Handle<JSFunction> f = GetFunction("f");
  // There should be one IC.
366 367
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
368
  FeedbackSlot slot(0);
369
  FeedbackNexus nexus(feedback_vector, slot);
370
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
371 372

  CompileRun("f(foo); f(foo);");
373
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
374
  CHECK_EQ(3, nexus.GetCallCount());
375 376 377

  // Send the IC megamorphic, but we should still have incrementing counts.
  CompileRun("f(function() { return 12; });");
378
  CHECK_EQ(GENERIC, nexus.ic_state());
379
  CHECK_EQ(4, nexus.GetCallCount());
380 381 382
}

TEST(VectorConstructCounts) {
383
  if (!i::FLAG_use_ic) return;
384
  if (i::FLAG_always_opt) return;
385
  FLAG_allow_natives_syntax = true;
386

387 388 389 390
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
391

392
  // Make sure function f has a call that uses a type feedback slot.
393 394
  CompileRun(
      "function Foo() {}"
395
      "%EnsureFeedbackVectorForFunction(f);"
396
      "function f(a) { new a(); } f(Foo);");
397
  Handle<JSFunction> f = GetFunction("f");
398 399
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
400

401
  FeedbackSlot slot(0);
402
  FeedbackNexus nexus(feedback_vector, slot);
403
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
404

405
  CHECK(feedback_vector->Get(slot)->IsWeak());
406 407

  CompileRun("f(Foo); f(Foo);");
408
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
409
  CHECK_EQ(3, nexus.GetCallCount());
410 411 412

  // Send the IC megamorphic, but we should still have incrementing counts.
  CompileRun("f(function() {});");
413
  CHECK_EQ(GENERIC, nexus.ic_state());
414 415 416 417
  CHECK_EQ(4, nexus.GetCallCount());
}

TEST(VectorSpeculationMode) {
418
  if (!i::FLAG_use_ic) return;
419
  if (i::FLAG_always_opt) return;
420
  FLAG_allow_natives_syntax = true;
421

422 423 424 425 426 427 428 429
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();

  // Make sure function f has a call that uses a type feedback slot.
  CompileRun(
      "function Foo() {}"
430
      "%EnsureFeedbackVectorForFunction(f);"
431 432 433 434 435 436
      "function f(a) { new a(); } f(Foo);");
  Handle<JSFunction> f = GetFunction("f");
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);

  FeedbackSlot slot(0);
437
  FeedbackNexus nexus(feedback_vector, slot);
438
  CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
439 440 441

  CompileRun("f(Foo); f(Foo);");
  CHECK_EQ(3, nexus.GetCallCount());
442
  CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
443

444 445
  nexus.SetSpeculationMode(SpeculationMode::kDisallowSpeculation);
  CHECK_EQ(SpeculationMode::kDisallowSpeculation, nexus.GetSpeculationMode());
446 447
  CHECK_EQ(3, nexus.GetCallCount());

448 449
  nexus.SetSpeculationMode(SpeculationMode::kAllowSpeculation);
  CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
450
  CHECK_EQ(3, nexus.GetCallCount());
451
}
452 453

TEST(VectorLoadICStates) {
454
  if (!i::FLAG_use_ic) return;
455
  if (i::FLAG_always_opt) return;
456
  FLAG_allow_natives_syntax = true;
457

458 459 460 461 462 463 464 465
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();

  // Make sure function f has a call that uses a type feedback slot.
  CompileRun(
      "var o = { foo: 3 };"
466
      "%EnsureFeedbackVectorForFunction(f);"
467
      "function f(a) { return a.foo; } f(o);");
468
  Handle<JSFunction> f = GetFunction("f");
469
  // There should be one IC.
470 471
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
472
  FeedbackSlot slot(0);
473
  FeedbackNexus nexus(feedback_vector, slot);
474

475
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
476
  // Verify that the monomorphic map is the one we expect.
477 478 479 480
  v8::MaybeLocal<v8::Value> v8_o =
      CcTest::global()->Get(context.local(), v8_str("o"));
  Handle<JSObject> o =
      Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
481
  CHECK_EQ(o->map(), nexus.GetFirstMap());
482 483 484

  // Now go polymorphic.
  CompileRun("f({ blarg: 3, foo: 2 })");
485
  CHECK_EQ(POLYMORPHIC, nexus.ic_state());
486 487 488 489

  CompileRun(
      "delete o.foo;"
      "f(o)");
490
  CHECK_EQ(POLYMORPHIC, nexus.ic_state());
491 492

  CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
493
  CHECK_EQ(POLYMORPHIC, nexus.ic_state());
494
  MapHandles maps;
495
  nexus.ExtractMaps(&maps);
496
  CHECK_EQ(4, maps.size());
497 498 499

  // Finally driven megamorphic.
  CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
500
  CHECK_EQ(MEGAMORPHIC, nexus.ic_state());
501
  CHECK(nexus.GetFirstMap().is_null());
502

503
  // After a collection, state should not be reset to PREMONOMORPHIC.
504
  CcTest::CollectAllGarbage();
505
  CHECK_EQ(MEGAMORPHIC, nexus.ic_state());
506
}
507

508
TEST(VectorLoadGlobalICSlotSharing) {
509
  if (!i::FLAG_use_ic) return;
510
  if (i::FLAG_always_opt) return;
511
  FLAG_allow_natives_syntax = true;
512

513 514 515 516 517
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();

518 519
  // Function f has 5 LoadGlobalICs: 3 for {o} references outside of "typeof"
  // operator and 2 for {o} references inside "typeof" operator.
520
  CompileRun(
521
      "o = 10;"
522
      "function f() {"
523
      "  var x = o || 10;"
524 525
      "  var y = typeof o;"
      "  return o , typeof o, x , y, o;"
526
      "}"
527
      "%EnsureFeedbackVectorForFunction(f);"
528
      "f();");
529
  Handle<JSFunction> f = GetFunction("f");
530 531
  // There should be two IC slots for {o} references outside and inside
  // typeof operator respectively.
532 533
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
534
  FeedbackVectorHelper helper(feedback_vector);
535
  CHECK_EQ(2, helper.slot_count());
536 537
  CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
  CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalInsideTypeof);
538 539
  FeedbackSlot slot1 = helper.slot(0);
  FeedbackSlot slot2 = helper.slot(1);
540 541
  CHECK_EQ(MONOMORPHIC, FeedbackNexus(feedback_vector, slot1).ic_state());
  CHECK_EQ(MONOMORPHIC, FeedbackNexus(feedback_vector, slot2).ic_state());
542 543 544
}


545
TEST(VectorLoadICOnSmi) {
546
  if (!i::FLAG_use_ic) return;
547
  if (i::FLAG_always_opt) return;
548
  FLAG_allow_natives_syntax = true;
549

550 551 552 553 554 555 556 557 558
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();

  // Make sure function f has a call that uses a type feedback slot.
  CompileRun(
      "var o = { foo: 3 };"
559
      "%EnsureFeedbackVectorForFunction(f);"
560
      "function f(a) { return a.foo; } f(34);");
561
  Handle<JSFunction> f = GetFunction("f");
562
  // There should be one IC.
563 564
  Handle<FeedbackVector> feedback_vector =
      Handle<FeedbackVector>(f->feedback_vector(), isolate);
565
  FeedbackSlot slot(0);
566
  FeedbackNexus nexus(feedback_vector, slot);
567
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
568
  // Verify that the monomorphic map is the one we expect.
569
  Map number_map = ReadOnlyRoots(heap).heap_number_map();
570
  CHECK_EQ(number_map, nexus.GetFirstMap());
571 572 573

  // Now go polymorphic on o.
  CompileRun("f(o)");
574
  CHECK_EQ(POLYMORPHIC, nexus.ic_state());
575

576
  MapHandles maps;
577
  nexus.ExtractMaps(&maps);
578
  CHECK_EQ(2, maps.size());
579 580

  // One of the maps should be the o map.
581 582 583 584
  v8::MaybeLocal<v8::Value> v8_o =
      CcTest::global()->Get(context.local(), v8_str("o"));
  Handle<JSObject> o =
      Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
585 586
  bool number_map_found = false;
  bool o_map_found = false;
587
  for (Handle<Map> current : maps) {
588 589 590 591 592 593 594 595 596
    if (*current == number_map)
      number_map_found = true;
    else if (*current == o->map())
      o_map_found = true;
  }
  CHECK(number_map_found && o_map_found);

  // The degree of polymorphism doesn't change.
  CompileRun("f(100)");
597
  CHECK_EQ(POLYMORPHIC, nexus.ic_state());
598
  MapHandles maps2;
599
  nexus.ExtractMaps(&maps2);
600
  CHECK_EQ(2, maps2.size());
601
}
602 603 604


TEST(ReferenceContextAllocatesNoSlots) {
605
  if (!i::FLAG_use_ic) return;
606
  if (i::FLAG_always_opt) return;
607
  FLAG_allow_natives_syntax = true;
608

609 610 611 612 613
  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());
  Isolate* isolate = CcTest::i_isolate();

614 615 616 617 618 619 620
  {
    CompileRun(
        "function testvar(x) {"
        "  y = x;"
        "  y = a;"
        "  return y;"
        "}"
621
        "%EnsureFeedbackVectorForFunction(testvar);"
622 623 624 625 626 627
        "a = 3;"
        "testvar({});");

    Handle<JSFunction> f = GetFunction("testvar");

    // There should be two LOAD_ICs, one for a and one for y at the end.
628
    Handle<FeedbackVector> feedback_vector =
629
        handle(f->feedback_vector(), isolate);
630
    FeedbackVectorHelper helper(feedback_vector);
631
    CHECK_EQ(3, helper.slot_count());
632
    CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kStoreGlobalSloppy);
633
    CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
634
    CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
635
  }
636

637 638 639
  {
    CompileRun(
        "function testprop(x) {"
640
        "  'use strict';"
641 642
        "  x.blue = a;"
        "}"
643
        "%EnsureFeedbackVectorForFunction(testprop);"
644
        "testprop({ blue: 3 });");
645

646
    Handle<JSFunction> f = GetFunction("testprop");
647

648
    // There should be one LOAD_IC, for the load of a.
649
    Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
650
    FeedbackVectorHelper helper(feedback_vector);
651
    CHECK_EQ(2, helper.slot_count());
652
    CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
653
    CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreNamedStrict);
654
  }
655

656 657 658 659 660 661
  {
    CompileRun(
        "function testpropfunc(x) {"
        "  x().blue = a;"
        "  return x().blue;"
        "}"
662
        "%EnsureFeedbackVectorForFunction(testpropfunc);"
663 664 665 666 667
        "function makeresult() { return { blue: 3 }; }"
        "testpropfunc(makeresult);");

    Handle<JSFunction> f = GetFunction("testpropfunc");

668 669
    // There should be 1 LOAD_GLOBAL_IC to load x (in both cases), 2 CALL_ICs
    // to call x and a LOAD_IC to load blue.
670
    Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
671
    FeedbackVectorHelper helper(feedback_vector);
672
    CHECK_EQ(5, helper.slot_count());
673 674
    CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kCall);
    CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
675
    CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kStoreNamedSloppy);
676 677
    CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kCall);
    CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kLoadProperty);
678
  }
679

680 681 682 683 684 685
  {
    CompileRun(
        "function testkeyedprop(x) {"
        "  x[0] = a;"
        "  return x[0];"
        "}"
686
        "%EnsureFeedbackVectorForFunction(testkeyedprop);"
687 688 689 690
        "testkeyedprop([0, 1, 2]);");

    Handle<JSFunction> f = GetFunction("testkeyedprop");

691 692
    // There should be 1 LOAD_GLOBAL_ICs for the load of a, and one
    // KEYED_LOAD_IC for the load of x[0] in the return statement.
693
    Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
694
    FeedbackVectorHelper helper(feedback_vector);
695
    CHECK_EQ(3, helper.slot_count());
696 697 698
    CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
    CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreKeyedSloppy);
    CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed);
699 700 701 702 703 704 705 706 707
  }

  {
    CompileRun(
        "function testkeyedprop(x) {"
        "  'use strict';"
        "  x[0] = a;"
        "  return x[0];"
        "}"
708
        "%EnsureFeedbackVectorForFunction(testkeyedprop);"
709 710 711 712 713 714
        "testkeyedprop([0, 1, 2]);");

    Handle<JSFunction> f = GetFunction("testkeyedprop");

    // There should be 1 LOAD_GLOBAL_ICs for the load of a, and one
    // KEYED_LOAD_IC for the load of x[0] in the return statement.
715
    Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
716 717
    FeedbackVectorHelper helper(feedback_vector);
    CHECK_EQ(3, helper.slot_count());
718 719 720
    CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
    CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreKeyedStrict);
    CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed);
721
  }
722

723 724 725
  {
    CompileRun(
        "function testcompound(x) {"
726
        "  'use strict';"
727 728 729
        "  x.old = x.young = x.in_between = a;"
        "  return x.old + x.young;"
        "}"
730
        "%EnsureFeedbackVectorForFunction(testcompound);"
731 732 733 734
        "testcompound({ old: 3, young: 3, in_between: 3 });");

    Handle<JSFunction> f = GetFunction("testcompound");

735 736
    // There should be 1 LOAD_GLOBAL_IC for load of a and 2 LOAD_ICs, for load
    // of x.old and x.young.
737
    Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
738
    FeedbackVectorHelper helper(feedback_vector);
739
    CHECK_EQ(7, helper.slot_count());
740
    CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
741 742 743
    CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreNamedStrict);
    CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kStoreNamedStrict);
    CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kStoreNamedStrict);
744
    CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kBinaryOp);
745
    CHECK_SLOT_KIND(helper, 5, FeedbackSlotKind::kLoadProperty);
746
    CHECK_SLOT_KIND(helper, 6, FeedbackSlotKind::kLoadProperty);
747 748 749 750 751
  }
}


TEST(VectorStoreICBasic) {
752
  if (!i::FLAG_use_ic) return;
753
  if (i::FLAG_always_opt) return;
754
  FLAG_allow_natives_syntax = true;
755 756 757 758 759 760 761 762

  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());

  CompileRun(
      "function f(a) {"
      "  a.foo = 5;"
763 764
      "};"
      "%EnsureFeedbackVectorForFunction(f);"
765 766 767 768 769 770
      "var a = { foo: 3 };"
      "f(a);"
      "f(a);"
      "f(a);");
  Handle<JSFunction> f = GetFunction("f");
  // There should be one IC slot.
771
  Handle<FeedbackVector> feedback_vector(f->feedback_vector(), f->GetIsolate());
772 773
  FeedbackVectorHelper helper(feedback_vector);
  CHECK_EQ(1, helper.slot_count());
774
  FeedbackSlot slot(0);
775
  FeedbackNexus nexus(feedback_vector, slot);
776
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
777
}
778

779
TEST(StoreOwnIC) {
780
  if (!i::FLAG_use_ic) return;
781
  if (i::FLAG_always_opt) return;
782
  FLAG_allow_natives_syntax = true;
783 784 785 786 787 788 789 790 791

  CcTest::InitializeVM();
  LocalContext context;
  v8::HandleScope scope(context->GetIsolate());

  CompileRun(
      "function f(v) {"
      "  return {a: 0, b: v, c: 0};"
      "}"
792
      "%EnsureFeedbackVectorForFunction(f);"
793 794 795 796 797
      "f(1);"
      "f(2);"
      "f(3);");
  Handle<JSFunction> f = GetFunction("f");
  // There should be one IC slot.
798
  Handle<FeedbackVector> feedback_vector(f->feedback_vector(), f->GetIsolate());
799 800 801 802
  FeedbackVectorHelper helper(feedback_vector);
  CHECK_EQ(2, helper.slot_count());
  CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLiteral);
  CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreOwnNamed);
803
  FeedbackNexus nexus(feedback_vector, helper.slot(1));
804
  CHECK_EQ(MONOMORPHIC, nexus.ic_state());
805 806
}

807
}  // namespace
808 809 810

}  // namespace internal
}  // namespace v8