// 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. #include "src/init/v8.h" #include "test/cctest/cctest.h" #include "src/api/api-inl.h" #include "src/codegen/macro-assembler.h" #include "src/debug/debug.h" #include "src/execution/execution.h" #include "src/handles/global-handles.h" #include "src/heap/factory.h" #include "src/objects/feedback-cell-inl.h" #include "src/objects/objects-inl.h" #include "test/cctest/test-feedback-vector.h" namespace v8 { namespace internal { namespace { #define CHECK_SLOT_KIND(helper, index, expected_kind) \ CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index))); static Handle GetFunction(const char* name) { v8::MaybeLocal v8_f = CcTest::global()->Get( CcTest::isolate()->GetCurrentContext(), v8_str(name)); Handle f = Handle::cast(v8::Utils::OpenHandle(*v8_f.ToLocalChecked())); return f; } TEST(VectorStructure) { LocalContext context; v8::HandleScope scope(context->GetIsolate()); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); Zone zone(isolate->allocator(), ZONE_NAME); Handle vector; { FeedbackVectorSpec one_slot(&zone); one_slot.AddForInSlot(); vector = NewFeedbackVector(isolate, &one_slot); FeedbackVectorHelper helper(vector); CHECK_EQ(1, helper.slot_count()); } { FeedbackVectorSpec one_icslot(&zone); one_icslot.AddCallICSlot(); vector = NewFeedbackVector(isolate, &one_icslot); FeedbackVectorHelper helper(vector); CHECK_EQ(1, helper.slot_count()); } { FeedbackVectorSpec spec(&zone); for (int i = 0; i < 3; i++) { spec.AddForInSlot(); } for (int i = 0; i < 5; i++) { spec.AddCallICSlot(); } vector = NewFeedbackVector(isolate, &spec); FeedbackVectorHelper helper(vector); CHECK_EQ(8, helper.slot_count()); int index = vector->GetIndex(helper.slot(0)); CHECK_EQ(helper.slot(0), vector->ToSlot(index)); index = vector->GetIndex(helper.slot(3)); CHECK_EQ(helper.slot(3), vector->ToSlot(index)); index = vector->GetIndex(helper.slot(7)); CHECK_EQ(3 + 4 * FeedbackMetadata::GetSlotSize(FeedbackSlotKind::kCall), index); CHECK_EQ(helper.slot(7), vector->ToSlot(index)); CHECK_EQ(3 + 5 * FeedbackMetadata::GetSlotSize(FeedbackSlotKind::kCall), vector->length()); } { FeedbackVectorSpec spec(&zone); spec.AddForInSlot(); spec.AddCreateClosureSlot(); spec.AddForInSlot(); vector = NewFeedbackVector(isolate, &spec); FeedbackVectorHelper helper(vector); FeedbackCell cell = *vector->GetClosureFeedbackCell(0); CHECK_EQ(cell.value(), *factory->undefined_value()); } } // 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(); Zone zone(isolate->allocator(), ZONE_NAME); FeedbackVectorSpec spec(&zone); // Set metadata. for (int i = 0; i < 40; i++) { switch (i % 4) { case 0: spec.AddForInSlot(); break; case 1: spec.AddCallICSlot(); break; case 2: spec.AddLoadICSlot(); break; case 3: spec.AddKeyedLoadICSlot(); break; } } Handle vector = NewFeedbackVector(isolate, &spec); FeedbackVectorHelper helper(vector); CHECK_EQ(40, helper.slot_count()); // Meanwhile set some feedback values and type feedback values to // verify the data structure remains intact. vector->SynchronizedSet(FeedbackSlot(0), MaybeObject::FromObject(*vector)); // Verify the metadata is correctly set up from the spec. for (int i = 0; i < 40; i++) { FeedbackSlotKind kind = vector->GetKind(helper.slot(i)); switch (i % 4) { case 0: CHECK_EQ(FeedbackSlotKind::kForIn, kind); break; case 1: CHECK_EQ(FeedbackSlotKind::kCall, kind); break; case 2: CHECK_EQ(FeedbackSlotKind::kLoadProperty, kind); break; case 3: CHECK_EQ(FeedbackSlotKind::kLoadKeyed, kind); break; } } } TEST(VectorCallICStates) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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; };" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { a(); } f(foo);"); Handle f = GetFunction("f"); // There should be one IC. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CompileRun("f(function() { return 16; })"); CHECK_EQ(GENERIC, nexus.ic_state()); // After a collection, state should remain GENERIC. CcTest::CollectAllGarbage(); CHECK_EQ(GENERIC, nexus.ic_state()); } // Test the Call IC states transfer with Function.prototype.apply TEST(VectorCallICStateApply) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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 F;" "%EnsureFeedbackVectorForFunction(foo);" "function foo() { return F.apply(null, arguments); }" "F = Math.min;" "foo();"); Handle foo = GetFunction("foo"); Handle F = GetFunction("F"); Handle feedback_vector = Handle(foo->feedback_vector(), isolate); FeedbackSlot slot(4); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent()); HeapObject heap_object; CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*F, heap_object); CompileRun( "F = Math.max;" "foo();"); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CHECK_EQ(CallFeedbackContent::kTarget, nexus.GetCallFeedbackContent()); CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*isolate->function_prototype_apply(), heap_object); CompileRun( "F.apply = (function () { return; });" "foo();"); CHECK_EQ(GENERIC, nexus.ic_state()); } TEST(VectorCallFeedback) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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; }" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { a(); } f(foo);"); Handle f = GetFunction("f"); Handle foo = GetFunction("foo"); // There should be one IC. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); HeapObject heap_object; CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*foo, heap_object); CcTest::CollectAllGarbage(); // It should stay monomorphic even after a GC. CHECK_EQ(MONOMORPHIC, nexus.ic_state()); } 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 f = GetFunction("f"); Handle a_foo = GetFunction("a_foo"); // There should be one IC. Handle feedback_vector = Handle(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()); } TEST(VectorCallFeedbackForArray) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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 f(a) { a(); };" "%EnsureFeedbackVectorForFunction(f);" "f(Array);"); Handle f = GetFunction("f"); // There should be one IC. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); HeapObject heap_object; CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*isolate->array_function(), heap_object); CcTest::CollectAllGarbage(); // It should stay monomorphic even after a GC. CHECK_EQ(MONOMORPHIC, nexus.ic_state()); } 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 = v8::Utils::OpenHandle(*CompileRun(src)); i::Handle f = i::Handle::cast(i_object); Handle feedback_vector = Handle(f->feedback_vector(), isolate); return feedback_vector->length(); } TEST(OneShotCallICSlotCount) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; if (i::FLAG_lazy_feedback_allocation) return; FLAG_allow_natives_syntax = true; 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); } TEST(VectorCallCounts) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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; }" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { a(); } f(foo);"); Handle f = GetFunction("f"); // There should be one IC. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CompileRun("f(foo); f(foo);"); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CHECK_EQ(3, nexus.GetCallCount()); // Send the IC megamorphic, but we should still have incrementing counts. CompileRun("f(function() { return 12; });"); CHECK_EQ(GENERIC, nexus.ic_state()); CHECK_EQ(4, nexus.GetCallCount()); } TEST(VectorConstructCounts) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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() {}" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { new a(); } f(Foo);"); Handle f = GetFunction("f"); Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CHECK(feedback_vector->Get(slot)->IsWeak()); CompileRun("f(Foo); f(Foo);"); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); CHECK_EQ(3, nexus.GetCallCount()); // Send the IC megamorphic, but we should still have incrementing counts. CompileRun("f(function() {});"); CHECK_EQ(GENERIC, nexus.ic_state()); CHECK_EQ(4, nexus.GetCallCount()); } TEST(VectorSpeculationMode) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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() {}" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { new a(); } f(Foo);"); Handle f = GetFunction("f"); Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode()); CompileRun("f(Foo); f(Foo);"); CHECK_EQ(3, nexus.GetCallCount()); CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode()); nexus.SetSpeculationMode(SpeculationMode::kDisallowSpeculation); CHECK_EQ(SpeculationMode::kDisallowSpeculation, nexus.GetSpeculationMode()); CHECK_EQ(3, nexus.GetCallCount()); nexus.SetSpeculationMode(SpeculationMode::kAllowSpeculation); CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode()); CHECK_EQ(3, nexus.GetCallCount()); } TEST(VectorLoadICStates) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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 };" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { return a.foo; } f(o);"); Handle f = GetFunction("f"); // There should be one IC. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); // Verify that the monomorphic map is the one we expect. v8::MaybeLocal v8_o = CcTest::global()->Get(context.local(), v8_str("o")); Handle o = Handle::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked())); CHECK_EQ(o->map(), nexus.GetFirstMap()); // Now go polymorphic. CompileRun("f({ blarg: 3, foo: 2 })"); CHECK_EQ(POLYMORPHIC, nexus.ic_state()); CompileRun( "delete o.foo;" "f(o)"); CHECK_EQ(POLYMORPHIC, nexus.ic_state()); CompileRun("f({ blarg: 3, torino: 10, foo: 2 })"); CHECK_EQ(POLYMORPHIC, nexus.ic_state()); MapHandles maps; nexus.ExtractMaps(&maps); CHECK_EQ(4, maps.size()); // Finally driven megamorphic. CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })"); CHECK_EQ(MEGAMORPHIC, nexus.ic_state()); CHECK(nexus.GetFirstMap().is_null()); // After a collection, state should not be reset to PREMONOMORPHIC. CcTest::CollectAllGarbage(); CHECK_EQ(MEGAMORPHIC, nexus.ic_state()); } TEST(VectorLoadGlobalICSlotSharing) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; CcTest::InitializeVM(); LocalContext context; v8::HandleScope scope(context->GetIsolate()); Isolate* isolate = CcTest::i_isolate(); // Function f has 5 LoadGlobalICs: 3 for {o} references outside of "typeof" // operator and 2 for {o} references inside "typeof" operator. CompileRun( "o = 10;" "function f() {" " var x = o || 10;" " var y = typeof o;" " return o , typeof o, x , y, o;" "}" "%EnsureFeedbackVectorForFunction(f);" "f();"); Handle f = GetFunction("f"); // There should be two IC slots for {o} references outside and inside // typeof operator respectively. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(2, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalInsideTypeof); FeedbackSlot slot1 = helper.slot(0); FeedbackSlot slot2 = helper.slot(1); CHECK_EQ(MONOMORPHIC, FeedbackNexus(feedback_vector, slot1).ic_state()); CHECK_EQ(MONOMORPHIC, FeedbackNexus(feedback_vector, slot2).ic_state()); } TEST(VectorLoadICOnSmi) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; 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 };" "%EnsureFeedbackVectorForFunction(f);" "function f(a) { return a.foo; } f(34);"); Handle f = GetFunction("f"); // There should be one IC. Handle feedback_vector = Handle(f->feedback_vector(), isolate); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); // Verify that the monomorphic map is the one we expect. Map number_map = ReadOnlyRoots(heap).heap_number_map(); CHECK_EQ(number_map, nexus.GetFirstMap()); // Now go polymorphic on o. CompileRun("f(o)"); CHECK_EQ(POLYMORPHIC, nexus.ic_state()); MapHandles maps; nexus.ExtractMaps(&maps); CHECK_EQ(2, maps.size()); // One of the maps should be the o map. v8::MaybeLocal v8_o = CcTest::global()->Get(context.local(), v8_str("o")); Handle o = Handle::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked())); bool number_map_found = false; bool o_map_found = false; for (Handle current : maps) { 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)"); CHECK_EQ(POLYMORPHIC, nexus.ic_state()); MapHandles maps2; nexus.ExtractMaps(&maps2); CHECK_EQ(2, maps2.size()); } TEST(ReferenceContextAllocatesNoSlots) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; CcTest::InitializeVM(); LocalContext context; v8::HandleScope scope(context->GetIsolate()); Isolate* isolate = CcTest::i_isolate(); { CompileRun( "function testvar(x) {" " y = x;" " y = a;" " return y;" "}" "%EnsureFeedbackVectorForFunction(testvar);" "a = 3;" "testvar({});"); Handle f = GetFunction("testvar"); // There should be two LOAD_ICs, one for a and one for y at the end. Handle feedback_vector = handle(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(3, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kStoreGlobalSloppy); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); } { CompileRun( "function testprop(x) {" " 'use strict';" " x.blue = a;" "}" "%EnsureFeedbackVectorForFunction(testprop);" "testprop({ blue: 3 });"); Handle f = GetFunction("testprop"); // There should be one LOAD_IC, for the load of a. Handle feedback_vector(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(2, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreNamedStrict); } { CompileRun( "function testpropfunc(x) {" " x().blue = a;" " return x().blue;" "}" "%EnsureFeedbackVectorForFunction(testpropfunc);" "function makeresult() { return { blue: 3 }; }" "testpropfunc(makeresult);"); Handle f = GetFunction("testpropfunc"); // 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. Handle feedback_vector(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(5, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kCall); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kStoreNamedSloppy); CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kCall); CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kLoadProperty); } { CompileRun( "function testkeyedprop(x) {" " x[0] = a;" " return x[0];" "}" "%EnsureFeedbackVectorForFunction(testkeyedprop);" "testkeyedprop([0, 1, 2]);"); Handle 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. Handle feedback_vector(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(3, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreKeyedSloppy); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed); } { CompileRun( "function testkeyedprop(x) {" " 'use strict';" " x[0] = a;" " return x[0];" "}" "%EnsureFeedbackVectorForFunction(testkeyedprop);" "testkeyedprop([0, 1, 2]);"); Handle 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. Handle feedback_vector(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(3, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreKeyedStrict); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed); } { CompileRun( "function testcompound(x) {" " 'use strict';" " x.old = x.young = x.in_between = a;" " return x.old + x.young;" "}" "%EnsureFeedbackVectorForFunction(testcompound);" "testcompound({ old: 3, young: 3, in_between: 3 });"); Handle f = GetFunction("testcompound"); // There should be 1 LOAD_GLOBAL_IC for load of a and 2 LOAD_ICs, for load // of x.old and x.young. Handle feedback_vector(f->feedback_vector(), isolate); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(7, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreNamedStrict); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kStoreNamedStrict); CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kStoreNamedStrict); CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kBinaryOp); CHECK_SLOT_KIND(helper, 5, FeedbackSlotKind::kLoadProperty); CHECK_SLOT_KIND(helper, 6, FeedbackSlotKind::kLoadProperty); } } TEST(VectorStoreICBasic) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; CcTest::InitializeVM(); LocalContext context; v8::HandleScope scope(context->GetIsolate()); CompileRun( "function f(a) {" " a.foo = 5;" "};" "%EnsureFeedbackVectorForFunction(f);" "var a = { foo: 3 };" "f(a);" "f(a);" "f(a);"); Handle f = GetFunction("f"); // There should be one IC slot. Handle feedback_vector(f->feedback_vector(), f->GetIsolate()); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(1, helper.slot_count()); FeedbackSlot slot(0); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); } TEST(StoreOwnIC) { if (!i::FLAG_use_ic) return; if (i::FLAG_always_opt) return; FLAG_allow_natives_syntax = true; CcTest::InitializeVM(); LocalContext context; v8::HandleScope scope(context->GetIsolate()); CompileRun( "function f(v) {" " return {a: 0, b: v, c: 0};" "}" "%EnsureFeedbackVectorForFunction(f);" "f(1);" "f(2);" "f(3);"); Handle f = GetFunction("f"); // There should be one IC slot. Handle feedback_vector(f->feedback_vector(), f->GetIsolate()); FeedbackVectorHelper helper(feedback_vector); CHECK_EQ(2, helper.slot_count()); CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLiteral); CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreOwnNamed); FeedbackNexus nexus(feedback_vector, helper.slot(1)); CHECK_EQ(MONOMORPHIC, nexus.ic_state()); } } // namespace } // namespace internal } // namespace v8