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.
6 #include "test/cctest/cctest.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"
16 using namespace v8::internal;
20 #define CHECK_SLOT_KIND(vector, slot, expected_kind) \
21 CHECK_EQ(expected_kind, vector->GetKind(FeedbackVectorICSlot(slot)));
24 TEST(VectorStructure) {
26 v8::HandleScope scope(context->GetIsolate());
27 Isolate* isolate = CcTest::i_isolate();
28 Factory* factory = isolate->factory();
29 Zone* zone = isolate->runtime_zone();
31 // Empty vectors are the empty fixed array.
32 StaticFeedbackVectorSpec empty;
33 Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&empty);
34 CHECK(Handle<FixedArray>::cast(vector)
35 .is_identical_to(factory->empty_fixed_array()));
36 // Which can nonetheless be queried.
37 CHECK_EQ(0, vector->ic_with_type_info_count());
38 CHECK_EQ(0, vector->ic_generic_count());
39 CHECK_EQ(0, vector->Slots());
40 CHECK_EQ(0, vector->ICSlots());
42 FeedbackVectorSpec one_slot(zone);
43 one_slot.AddStubSlot();
44 vector = factory->NewTypeFeedbackVector(&one_slot);
45 CHECK_EQ(1, vector->Slots());
46 CHECK_EQ(0, vector->ICSlots());
48 FeedbackVectorSpec one_icslot(zone);
49 one_icslot.AddSlot(FeedbackVectorSlotKind::CALL_IC);
50 vector = factory->NewTypeFeedbackVector(&one_icslot);
51 CHECK_EQ(0, vector->Slots());
52 CHECK_EQ(1, vector->ICSlots());
54 FeedbackVectorSpec spec(zone);
56 spec.AddSlots(FeedbackVectorSlotKind::CALL_IC, 5);
57 vector = factory->NewTypeFeedbackVector(&spec);
58 CHECK_EQ(3, vector->Slots());
59 CHECK_EQ(5, vector->ICSlots());
61 int metadata_length = vector->ic_metadata_length();
62 CHECK(metadata_length > 0);
64 int index = vector->GetIndex(FeedbackVectorSlot(0));
66 CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length, index);
67 CHECK(FeedbackVectorSlot(0) == vector->ToSlot(index));
69 index = vector->GetIndex(FeedbackVectorICSlot(0));
71 TypeFeedbackVector::kReservedIndexCount + metadata_length + 3);
72 CHECK(FeedbackVectorICSlot(0) == vector->ToICSlot(index));
73 CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length + 3 +
74 5 * TypeFeedbackVector::elements_per_ic_slot(),
79 // IC slots need an encoding to recognize what is in there.
80 TEST(VectorICMetadata) {
82 v8::HandleScope scope(context->GetIsolate());
83 Isolate* isolate = CcTest::i_isolate();
84 Factory* factory = isolate->factory();
85 Zone* zone = isolate->runtime_zone();
87 FeedbackVectorSpec spec(zone);
89 spec.AddStubSlots(10);
90 for (int i = 0; i < 30; i++) {
93 spec.AddSlot(FeedbackVectorSlotKind::CALL_IC);
96 spec.AddSlot(FeedbackVectorSlotKind::LOAD_IC);
99 spec.AddSlot(FeedbackVectorSlotKind::KEYED_LOAD_IC);
104 Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&spec);
105 CHECK_EQ(10, vector->Slots());
106 CHECK_EQ(3 * 10, vector->ICSlots());
108 // Meanwhile set some feedback values and type feedback values to
109 // verify the data structure remains intact.
110 vector->change_ic_with_type_info_count(100);
111 vector->change_ic_generic_count(3333);
112 vector->Set(FeedbackVectorSlot(0), *vector);
114 // Verify the metadata is correctly set up from the spec.
115 for (int i = 0; i < 30; i++) {
116 FeedbackVectorSlotKind kind = vector->GetKind(FeedbackVectorICSlot(i));
118 CHECK_EQ(FeedbackVectorSlotKind::CALL_IC, kind);
119 } else if (i % 3 == 1) {
120 CHECK_EQ(FeedbackVectorSlotKind::LOAD_IC, kind);
122 CHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, kind);
128 TEST(VectorSlotClearing) {
129 LocalContext context;
130 v8::HandleScope scope(context->GetIsolate());
131 Isolate* isolate = CcTest::i_isolate();
132 Factory* factory = isolate->factory();
133 Zone* zone = isolate->runtime_zone();
135 // We only test clearing FeedbackVectorSlots, not FeedbackVectorICSlots.
136 // The reason is that FeedbackVectorICSlots need a full code environment
137 // to fully test (See VectorICProfilerStatistics test below).
138 FeedbackVectorSpec spec(zone);
139 spec.AddStubSlots(5);
140 Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(&spec);
142 // Fill with information
143 vector->Set(FeedbackVectorSlot(0), Smi::FromInt(1));
144 Handle<WeakCell> cell = factory->NewWeakCell(factory->fixed_array_map());
145 vector->Set(FeedbackVectorSlot(1), *cell);
146 Handle<AllocationSite> site = factory->NewAllocationSite();
147 vector->Set(FeedbackVectorSlot(2), *site);
149 // GC time clearing leaves slots alone.
150 vector->ClearSlotsAtGCTime(NULL);
151 Object* obj = vector->Get(FeedbackVectorSlot(1));
152 CHECK(obj->IsWeakCell() && !WeakCell::cast(obj)->cleared());
154 vector->ClearSlots(NULL);
156 // The feedback vector slots are cleared. AllocationSites are still granted
157 // an exemption from clearing, as are smis.
158 CHECK_EQ(Smi::FromInt(1), vector->Get(FeedbackVectorSlot(0)));
159 CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),
160 vector->Get(FeedbackVectorSlot(1)));
161 CHECK(vector->Get(FeedbackVectorSlot(2))->IsAllocationSite());
165 TEST(VectorICProfilerStatistics) {
166 if (i::FLAG_always_opt) return;
167 CcTest::InitializeVM();
168 LocalContext context;
169 v8::HandleScope scope(context->GetIsolate());
170 Isolate* isolate = CcTest::i_isolate();
171 Heap* heap = isolate->heap();
173 // Make sure function f has a call that uses a type feedback slot.
176 "function f(a) { a(); } f(fun);");
177 Handle<JSFunction> f = v8::Utils::OpenHandle(
178 *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
179 // There should be one IC.
180 Handle<Code> code = handle(f->shared()->code(), isolate);
181 TypeFeedbackInfo* feedback_info =
182 TypeFeedbackInfo::cast(code->type_feedback_info());
183 CHECK_EQ(1, feedback_info->ic_total_count());
184 CHECK_EQ(0, feedback_info->ic_with_type_info_count());
185 CHECK_EQ(0, feedback_info->ic_generic_count());
186 Handle<TypeFeedbackVector> feedback_vector =
187 handle(f->shared()->feedback_vector(), isolate);
189 CallICNexus nexus(feedback_vector, FeedbackVectorICSlot(ic_slot));
190 CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
191 CHECK_EQ(0, feedback_vector->ic_generic_count());
193 // Now send the information generic.
194 CompileRun("f(Object);");
195 CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
196 CHECK_EQ(1, feedback_vector->ic_generic_count());
198 // A collection will not affect the site.
199 heap->CollectAllGarbage();
200 CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
201 CHECK_EQ(1, feedback_vector->ic_generic_count());
203 // The Array function is special. A call to array remains monomorphic
204 // and isn't cleared by gc because an AllocationSite is being held.
205 // Clear the IC manually in order to test this case.
207 CompileRun("f(Array);");
208 CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
209 CHECK_EQ(0, feedback_vector->ic_generic_count());
212 CHECK(nexus.GetFeedback()->IsAllocationSite());
213 heap->CollectAllGarbage();
214 CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
215 CHECK_EQ(0, feedback_vector->ic_generic_count());
216 CHECK(nexus.GetFeedback()->IsAllocationSite());
220 TEST(VectorCallICStates) {
221 if (i::FLAG_always_opt) return;
222 CcTest::InitializeVM();
223 LocalContext context;
224 v8::HandleScope scope(context->GetIsolate());
225 Isolate* isolate = CcTest::i_isolate();
226 Heap* heap = isolate->heap();
228 // Make sure function f has a call that uses a type feedback slot.
230 "function foo() { return 17; }"
231 "function f(a) { a(); } f(foo);");
232 Handle<JSFunction> f = v8::Utils::OpenHandle(
233 *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
234 // There should be one IC.
235 Handle<TypeFeedbackVector> feedback_vector =
236 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
237 FeedbackVectorICSlot slot(0);
238 CallICNexus nexus(feedback_vector, slot);
239 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
240 // CallIC doesn't return map feedback.
241 CHECK(!nexus.FindFirstMap());
243 CompileRun("f(function() { return 16; })");
244 CHECK_EQ(GENERIC, nexus.StateFromFeedback());
246 // After a collection, state should remain GENERIC.
247 heap->CollectAllGarbage();
248 CHECK_EQ(GENERIC, nexus.StateFromFeedback());
250 // A call to Array is special, it contains an AllocationSite as feedback.
251 // Clear the IC manually in order to test this case.
252 nexus.Clear(f->shared()->code());
253 CompileRun("f(Array)");
254 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
255 CHECK(nexus.GetFeedback()->IsAllocationSite());
257 heap->CollectAllGarbage();
258 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
262 TEST(VectorLoadICStates) {
263 if (i::FLAG_always_opt) return;
264 CcTest::InitializeVM();
265 LocalContext context;
266 v8::HandleScope scope(context->GetIsolate());
267 Isolate* isolate = CcTest::i_isolate();
268 Heap* heap = isolate->heap();
270 // Make sure function f has a call that uses a type feedback slot.
272 "var o = { foo: 3 };"
273 "function f(a) { return a.foo; } f(o);");
274 Handle<JSFunction> f = v8::Utils::OpenHandle(
275 *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
276 // There should be one IC.
277 Handle<TypeFeedbackVector> feedback_vector =
278 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
279 FeedbackVectorICSlot slot(0);
280 LoadICNexus nexus(feedback_vector, slot);
281 CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
284 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
285 // Verify that the monomorphic map is the one we expect.
286 Handle<JSObject> o = v8::Utils::OpenHandle(
287 *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
288 CHECK_EQ(o->map(), nexus.FindFirstMap());
290 // Now go polymorphic.
291 CompileRun("f({ blarg: 3, foo: 2 })");
292 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
297 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
299 CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
300 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
302 nexus.FindAllMaps(&maps);
303 CHECK_EQ(4, maps.length());
305 // Finally driven megamorphic.
306 CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
307 CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
308 CHECK(!nexus.FindFirstMap());
310 // After a collection, state should not be reset to PREMONOMORPHIC.
311 heap->CollectAllGarbage();
312 CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
316 TEST(VectorLoadICSlotSharing) {
317 if (i::FLAG_always_opt) return;
318 CcTest::InitializeVM();
319 LocalContext context;
320 v8::HandleScope scope(context->GetIsolate());
321 Isolate* isolate = CcTest::i_isolate();
323 // Function f has 3 LoadICs, one for each o, but the ICs share the same
324 // feedback vector IC slot.
332 Handle<JSFunction> f = v8::Utils::OpenHandle(
333 *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
334 // There should be one IC slot.
335 Handle<TypeFeedbackVector> feedback_vector =
336 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
337 CHECK_EQ(1, feedback_vector->ICSlots());
338 FeedbackVectorICSlot slot(0);
339 LoadICNexus nexus(feedback_vector, slot);
340 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
344 TEST(VectorLoadICOnSmi) {
345 if (i::FLAG_always_opt) return;
346 CcTest::InitializeVM();
347 LocalContext context;
348 v8::HandleScope scope(context->GetIsolate());
349 Isolate* isolate = CcTest::i_isolate();
350 Heap* heap = isolate->heap();
352 // Make sure function f has a call that uses a type feedback slot.
354 "var o = { foo: 3 };"
355 "function f(a) { return a.foo; } f(o);");
356 Handle<JSFunction> f = v8::Utils::OpenHandle(
357 *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
358 // There should be one IC.
359 Handle<TypeFeedbackVector> feedback_vector =
360 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
361 FeedbackVectorICSlot slot(0);
362 LoadICNexus nexus(feedback_vector, slot);
363 CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
366 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
367 // Verify that the monomorphic map is the one we expect.
368 Map* number_map = heap->heap_number_map();
369 CHECK_EQ(number_map, nexus.FindFirstMap());
371 // Now go polymorphic on o.
373 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
376 nexus.FindAllMaps(&maps);
377 CHECK_EQ(2, maps.length());
379 // One of the maps should be the o map.
380 Handle<JSObject> o = v8::Utils::OpenHandle(
381 *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
382 bool number_map_found = false;
383 bool o_map_found = false;
384 for (int i = 0; i < maps.length(); i++) {
385 Handle<Map> current = maps[i];
386 if (*current == number_map)
387 number_map_found = true;
388 else if (*current == o->map())
391 CHECK(number_map_found && o_map_found);
393 // The degree of polymorphism doesn't change.
394 CompileRun("f(100)");
395 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
397 nexus.FindAllMaps(&maps2);
398 CHECK_EQ(2, maps2.length());
402 static Handle<JSFunction> GetFunction(const char* name) {
403 Handle<JSFunction> f = v8::Utils::OpenHandle(
404 *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str(name))));
409 TEST(ReferenceContextAllocatesNoSlots) {
410 if (i::FLAG_always_opt) return;
411 CcTest::InitializeVM();
412 LocalContext context;
413 v8::HandleScope scope(context->GetIsolate());
414 Isolate* isolate = CcTest::i_isolate();
417 "function testvar(x) {"
425 Handle<JSFunction> f = GetFunction("testvar");
427 // There should be two LOAD_ICs, one for a and one for y at the end.
428 Handle<TypeFeedbackVector> feedback_vector =
429 handle(f->shared()->feedback_vector(), isolate);
430 if (FLAG_vector_stores) {
431 CHECK_EQ(4, feedback_vector->ICSlots());
432 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::STORE_IC);
433 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::LOAD_IC);
434 CHECK_SLOT_KIND(feedback_vector, 2, FeedbackVectorSlotKind::STORE_IC);
435 CHECK_SLOT_KIND(feedback_vector, 3, FeedbackVectorSlotKind::LOAD_IC);
437 CHECK_EQ(2, feedback_vector->ICSlots());
438 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::LOAD_IC);
439 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::LOAD_IC);
443 "function testprop(x) {"
446 "testprop({ blue: 3 });");
448 f = GetFunction("testprop");
450 // There should be one LOAD_IC, for the load of a.
451 feedback_vector = handle(f->shared()->feedback_vector(), isolate);
452 if (FLAG_vector_stores) {
453 CHECK_EQ(2, feedback_vector->ICSlots());
455 CHECK_EQ(1, feedback_vector->ICSlots());
459 "function testpropfunc(x) {"
463 "function makeresult() { return { blue: 3 }; }"
464 "testpropfunc(makeresult);");
466 f = GetFunction("testpropfunc");
468 // There should be 2 LOAD_ICs and 2 CALL_ICs.
469 feedback_vector = handle(f->shared()->feedback_vector(), isolate);
470 if (FLAG_vector_stores) {
471 CHECK_EQ(5, feedback_vector->ICSlots());
472 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::CALL_IC);
473 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::LOAD_IC);
474 CHECK_SLOT_KIND(feedback_vector, 2, FeedbackVectorSlotKind::STORE_IC);
475 CHECK_SLOT_KIND(feedback_vector, 3, FeedbackVectorSlotKind::CALL_IC);
476 CHECK_SLOT_KIND(feedback_vector, 4, FeedbackVectorSlotKind::LOAD_IC);
478 CHECK_EQ(4, feedback_vector->ICSlots());
479 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::CALL_IC);
480 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::LOAD_IC);
481 CHECK_SLOT_KIND(feedback_vector, 2, FeedbackVectorSlotKind::CALL_IC);
482 CHECK_SLOT_KIND(feedback_vector, 3, FeedbackVectorSlotKind::LOAD_IC);
486 "function testkeyedprop(x) {"
490 "testkeyedprop([0, 1, 2]);");
492 f = GetFunction("testkeyedprop");
494 // There should be 1 LOAD_ICs for the load of a, and one KEYED_LOAD_IC for the
495 // load of x[0] in the return statement.
496 feedback_vector = handle(f->shared()->feedback_vector(), isolate);
497 if (FLAG_vector_stores) {
498 CHECK_EQ(3, feedback_vector->ICSlots());
499 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::LOAD_IC);
500 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::KEYED_STORE_IC);
501 CHECK_SLOT_KIND(feedback_vector, 2, FeedbackVectorSlotKind::KEYED_LOAD_IC);
503 CHECK_EQ(2, feedback_vector->ICSlots());
504 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::LOAD_IC);
505 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::KEYED_LOAD_IC);
509 "function testcompound(x) {"
510 " x.old = x.young = x.in_between = a;"
511 " return x.old + x.young;"
513 "testcompound({ old: 3, young: 3, in_between: 3 });");
515 f = GetFunction("testcompound");
517 // There should be 3 LOAD_ICs, for load of a and load of x.old and x.young.
518 feedback_vector = handle(f->shared()->feedback_vector(), isolate);
519 if (FLAG_vector_stores) {
520 CHECK_EQ(6, feedback_vector->ICSlots());
521 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::LOAD_IC);
522 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::STORE_IC);
523 CHECK_SLOT_KIND(feedback_vector, 2, FeedbackVectorSlotKind::STORE_IC);
524 CHECK_SLOT_KIND(feedback_vector, 3, FeedbackVectorSlotKind::STORE_IC);
525 CHECK_SLOT_KIND(feedback_vector, 4, FeedbackVectorSlotKind::LOAD_IC);
526 CHECK_SLOT_KIND(feedback_vector, 5, FeedbackVectorSlotKind::LOAD_IC);
528 CHECK_EQ(3, feedback_vector->ICSlots());
529 CHECK_SLOT_KIND(feedback_vector, 0, FeedbackVectorSlotKind::LOAD_IC);
530 CHECK_SLOT_KIND(feedback_vector, 1, FeedbackVectorSlotKind::LOAD_IC);
531 CHECK_SLOT_KIND(feedback_vector, 2, FeedbackVectorSlotKind::LOAD_IC);
536 TEST(VectorStoreICBasic) {
537 if (i::FLAG_always_opt) return;
538 if (!i::FLAG_vector_stores) return;
540 CcTest::InitializeVM();
541 LocalContext context;
542 v8::HandleScope scope(context->GetIsolate());
543 Isolate* isolate = CcTest::i_isolate();
549 "var a = { foo: 3 };"
553 Handle<JSFunction> f = GetFunction("f");
554 // There should be one IC slot.
555 Handle<TypeFeedbackVector> feedback_vector =
556 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
557 CHECK_EQ(1, feedback_vector->ICSlots());
558 FeedbackVectorICSlot slot(0);
559 StoreICNexus nexus(feedback_vector, slot);
560 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());