// Copyright 2022 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/api/api-inl.h" #include "src/execution/execution.h" #include "src/heap/factory.h" #include "src/objects/feedback-cell-inl.h" #include "src/objects/objects-inl.h" #include "test/unittests/test-utils.h" namespace v8 { namespace internal { class FeedbackVectorTest : public TestWithContext { protected: Handle GetFunction(const char* name) { v8::MaybeLocal v8_f = v8_context()->Global()->Get(v8_context(), NewString(name)); Handle f = Handle::cast(v8::Utils::OpenHandle(*v8_f.ToLocalChecked())); return f; } }; #define CHECK_SLOT_KIND(helper, index, expected_kind) \ CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index))); TEST_F(FeedbackVectorTest, VectorStructure) { v8::HandleScope scope(v8_isolate()); Isolate* isolate = 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_F(FeedbackVectorTest, VectorICMetadata) { v8::HandleScope scope(v8_isolate()); Isolate* isolate = 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_F(FeedbackVectorTest, VectorCallICStates) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); TryRunJS("f(function() { return 16; })"); CHECK_EQ(InlineCacheState::GENERIC, nexus.ic_state()); // After a collection, state should remain GENERIC. CollectAllGarbage(); CHECK_EQ(InlineCacheState::GENERIC, nexus.ic_state()); } // Test the Call IC states transfer with Function.prototype.apply TEST_F(FeedbackVectorTest, VectorCallICStateApply) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent()); HeapObject heap_object; CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*F, heap_object); TryRunJS( "F = Math.max;" "foo();"); CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(CallFeedbackContent::kTarget, nexus.GetCallFeedbackContent()); CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*isolate->function_prototype_apply(), heap_object); TryRunJS( "F.apply = (function () { return; });" "foo();"); CHECK_EQ(InlineCacheState::GENERIC, nexus.ic_state()); } TEST_F(FeedbackVectorTest, VectorCallFeedback) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); HeapObject heap_object; CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*foo, heap_object); CollectAllGarbage(); // It should stay monomorphic even after a GC. CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); } TEST_F(FeedbackVectorTest, VectorPolymorphicCallFeedback) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8_flags.lazy_feedback_allocation = false; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure the call feedback of a() in f() becomes polymorphic. TryRunJS( "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(InlineCacheState::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_F(FeedbackVectorTest, VectorCallFeedbackForArray) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); HeapObject heap_object; CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)); CHECK_EQ(*isolate->array_function(), heap_object); CollectAllGarbage(); // It should stay monomorphic even after a GC. CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); } TEST_F(FeedbackVectorTest, VectorCallCounts) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); TryRunJS("f(foo); f(foo);"); CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(3, nexus.GetCallCount()); // Send the IC megamorphic, but we should still have incrementing counts. TryRunJS("f(function() { return 12; });"); CHECK_EQ(InlineCacheState::GENERIC, nexus.ic_state()); CHECK_EQ(4, nexus.GetCallCount()); } TEST_F(FeedbackVectorTest, VectorConstructCounts) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK(feedback_vector->Get(slot)->IsWeak()); TryRunJS("f(Foo); f(Foo);"); CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(3, nexus.GetCallCount()); // Send the IC megamorphic, but we should still have incrementing counts. TryRunJS("f(function() {});"); CHECK_EQ(InlineCacheState::GENERIC, nexus.ic_state()); CHECK_EQ(4, nexus.GetCallCount()); } TEST_F(FeedbackVectorTest, VectorSpeculationMode) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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()); TryRunJS("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_F(FeedbackVectorTest, VectorCallSpeculationModeAndFeedbackContent) { if (!i::v8_flags.use_ic) return; if (!i::v8_flags.turbofan) return; if (i::v8_flags.always_turbofan) return; if (i::v8_flags.jitless) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); TryRunJS( "function min() { return Math.min.apply(null, arguments); }" "function f(x) { return min(x, 0); }" "%PrepareFunctionForOptimization(min);" "%PrepareFunctionForOptimization(f);" "f(1);"); Handle min = GetFunction("min"); Handle feedback_vector = Handle(min->feedback_vector(), isolate); FeedbackSlot slot(6); FeedbackNexus nexus(feedback_vector, slot); CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode()); CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent()); TryRunJS("%OptimizeFunctionOnNextCall(f); f(1);"); CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode()); CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent()); TryRunJS("f({});"); // Deoptimizes. CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); CHECK_EQ(SpeculationMode::kDisallowSpeculation, nexus.GetSpeculationMode()); CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent()); } TEST_F(FeedbackVectorTest, VectorLoadICStates) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); // Verify that the monomorphic map is the one we expect. v8::MaybeLocal v8_o = v8_context()->Global()->Get(v8_context(), NewString("o")); Handle o = Handle::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked())); CHECK_EQ(o->map(), nexus.GetFirstMap()); // Now go polymorphic. TryRunJS("f({ blarg: 3, foo: 2 })"); CHECK_EQ(InlineCacheState::POLYMORPHIC, nexus.ic_state()); TryRunJS( "delete o.foo;" "f(o)"); CHECK_EQ(InlineCacheState::POLYMORPHIC, nexus.ic_state()); TryRunJS("f({ blarg: 3, torino: 10, foo: 2 })"); CHECK_EQ(InlineCacheState::POLYMORPHIC, nexus.ic_state()); MapHandles maps; nexus.ExtractMaps(&maps); CHECK_EQ(4, maps.size()); // Finally driven megamorphic. TryRunJS("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })"); CHECK_EQ(InlineCacheState::MEGAMORPHIC, nexus.ic_state()); CHECK(nexus.GetFirstMap().is_null()); // After a collection, state should not be reset to PREMONOMORPHIC. CollectAllGarbage(); CHECK_EQ(InlineCacheState::MEGAMORPHIC, nexus.ic_state()); } TEST_F(FeedbackVectorTest, VectorLoadGlobalICSlotSharing) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); // Function f has 5 LoadGlobalICs: 3 for {o} references outside of "typeof" // operator and 2 for {o} references inside "typeof" operator. TryRunJS( "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(InlineCacheState::MONOMORPHIC, FeedbackNexus(feedback_vector, slot1).ic_state()); CHECK_EQ(InlineCacheState::MONOMORPHIC, FeedbackNexus(feedback_vector, slot2).ic_state()); } TEST_F(FeedbackVectorTest, VectorLoadICOnSmi) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); Heap* heap = isolate->heap(); // Make sure function f has a call that uses a type feedback slot. TryRunJS( "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(InlineCacheState::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. TryRunJS("f(o)"); CHECK_EQ(InlineCacheState::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 = v8_context()->Global()->Get(v8_context(), NewString("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. TryRunJS("f(100)"); CHECK_EQ(InlineCacheState::POLYMORPHIC, nexus.ic_state()); MapHandles maps2; nexus.ExtractMaps(&maps2); CHECK_EQ(2, maps2.size()); } TEST_F(FeedbackVectorTest, ReferenceContextAllocatesNoSlots) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); Isolate* isolate = i_isolate(); { TryRunJS( "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); } { TryRunJS( "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::kSetNamedStrict); } { TryRunJS( "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::kSetNamedSloppy); CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kCall); CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kLoadProperty); } { TryRunJS( "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::kSetKeyedSloppy); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed); } { TryRunJS( "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::kSetKeyedStrict); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed); } { TryRunJS( "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::kSetNamedStrict); CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kSetNamedStrict); CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kSetNamedStrict); CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kBinaryOp); CHECK_SLOT_KIND(helper, 5, FeedbackSlotKind::kLoadProperty); CHECK_SLOT_KIND(helper, 6, FeedbackSlotKind::kLoadProperty); } } TEST_F(FeedbackVectorTest, VectorStoreICBasic) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); TryRunJS( "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(InlineCacheState::MONOMORPHIC, nexus.ic_state()); } TEST_F(FeedbackVectorTest, DefineNamedOwnIC) { if (!i::v8_flags.use_ic) return; if (i::v8_flags.always_turbofan) return; v8_flags.allow_natives_syntax = true; v8::HandleScope scope(v8_isolate()); TryRunJS( "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::kDefineNamedOwn); FeedbackNexus nexus(feedback_vector, helper.slot(1)); CHECK_EQ(InlineCacheState::MONOMORPHIC, nexus.ic_state()); } } // namespace internal } // namespace v8