1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/v8.h"
6 #include "test/cctest/cctest.h"
7
8 #include "src/api.h"
9 #include "src/debug/debug.h"
10 #include "src/execution.h"
11 #include "src/factory.h"
12 #include "src/global-handles.h"
13 #include "src/macro-assembler.h"
14 #include "src/objects.h"
15 #include "test/cctest/test-feedback-vector.h"
16
17 using namespace v8::internal;
18
19 namespace {
20
21 #define CHECK_SLOT_KIND(helper, index, expected_kind) \
22 CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index)));
23
24
GetFunction(const char * name)25 static Handle<JSFunction> GetFunction(const char* name) {
26 v8::MaybeLocal<v8::Value> v8_f = CcTest::global()->Get(
27 v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str(name));
28 Handle<JSFunction> f =
29 Handle<JSFunction>::cast(v8::Utils::OpenHandle(*v8_f.ToLocalChecked()));
30 return f;
31 }
32
33
TEST(VectorStructure)34 TEST(VectorStructure) {
35 LocalContext context;
36 v8::HandleScope scope(context->GetIsolate());
37 Isolate* isolate = CcTest::i_isolate();
38 Factory* factory = isolate->factory();
39 Zone* zone = isolate->runtime_zone();
40
41 // Empty vectors are the empty fixed array.
42 StaticFeedbackVectorSpec empty;
43 Handle<TypeFeedbackVector> vector = NewTypeFeedbackVector(isolate, &empty);
44 CHECK(Handle<FixedArray>::cast(vector)
45 .is_identical_to(factory->empty_fixed_array()));
46 // Which can nonetheless be queried.
47 CHECK(vector->is_empty());
48
49 {
50 FeedbackVectorSpec one_slot(zone);
51 one_slot.AddGeneralSlot();
52 vector = NewTypeFeedbackVector(isolate, &one_slot);
53 FeedbackVectorHelper helper(vector);
54 CHECK_EQ(1, helper.slot_count());
55 }
56
57 {
58 FeedbackVectorSpec one_icslot(zone);
59 one_icslot.AddCallICSlot();
60 vector = NewTypeFeedbackVector(isolate, &one_icslot);
61 FeedbackVectorHelper helper(vector);
62 CHECK_EQ(1, helper.slot_count());
63 }
64
65 {
66 FeedbackVectorSpec spec(zone);
67 for (int i = 0; i < 3; i++) {
68 spec.AddGeneralSlot();
69 }
70 for (int i = 0; i < 5; i++) {
71 spec.AddCallICSlot();
72 }
73 vector = NewTypeFeedbackVector(isolate, &spec);
74 FeedbackVectorHelper helper(vector);
75 CHECK_EQ(8, helper.slot_count());
76
77 int index = vector->GetIndex(helper.slot(0));
78
79 CHECK_EQ(TypeFeedbackVector::kReservedIndexCount, index);
80 CHECK_EQ(helper.slot(0), vector->ToSlot(index));
81
82 index = vector->GetIndex(helper.slot(3));
83 CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + 3, index);
84 CHECK_EQ(helper.slot(3), vector->ToSlot(index));
85
86 index = vector->GetIndex(helper.slot(7));
87 CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + 3 +
88 4 * TypeFeedbackMetadata::GetSlotSize(
89 FeedbackVectorSlotKind::CALL_IC),
90 index);
91 CHECK_EQ(helper.slot(7), vector->ToSlot(index));
92
93 CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + 3 +
94 5 * TypeFeedbackMetadata::GetSlotSize(
95 FeedbackVectorSlotKind::CALL_IC),
96 vector->length());
97 }
98 }
99
100
101 // IC slots need an encoding to recognize what is in there.
TEST(VectorICMetadata)102 TEST(VectorICMetadata) {
103 LocalContext context;
104 v8::HandleScope scope(context->GetIsolate());
105 Isolate* isolate = CcTest::i_isolate();
106 Zone* zone = isolate->runtime_zone();
107
108 FeedbackVectorSpec spec(zone);
109 // Set metadata.
110 for (int i = 0; i < 40; i++) {
111 switch (i % 4) {
112 case 0:
113 spec.AddGeneralSlot();
114 break;
115 case 1:
116 spec.AddCallICSlot();
117 break;
118 case 2:
119 spec.AddLoadICSlot();
120 break;
121 case 3:
122 spec.AddKeyedLoadICSlot();
123 break;
124 }
125 }
126
127 Handle<TypeFeedbackVector> vector = NewTypeFeedbackVector(isolate, &spec);
128 FeedbackVectorHelper helper(vector);
129 CHECK_EQ(40, helper.slot_count());
130
131 // Meanwhile set some feedback values and type feedback values to
132 // verify the data structure remains intact.
133 vector->Set(FeedbackVectorSlot(0), *vector);
134
135 // Verify the metadata is correctly set up from the spec.
136 for (int i = 0; i < 40; i++) {
137 FeedbackVectorSlotKind kind = vector->GetKind(helper.slot(i));
138 switch (i % 4) {
139 case 0:
140 CHECK_EQ(FeedbackVectorSlotKind::GENERAL, kind);
141 break;
142 case 1:
143 CHECK_EQ(FeedbackVectorSlotKind::CALL_IC, kind);
144 break;
145 case 2:
146 CHECK_EQ(FeedbackVectorSlotKind::LOAD_IC, kind);
147 break;
148 case 3:
149 CHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, kind);
150 break;
151 }
152 }
153 }
154
155
TEST(VectorSlotClearing)156 TEST(VectorSlotClearing) {
157 LocalContext context;
158 v8::HandleScope scope(context->GetIsolate());
159 Isolate* isolate = CcTest::i_isolate();
160 Factory* factory = isolate->factory();
161 Zone* zone = isolate->runtime_zone();
162
163 // We only test clearing FeedbackVectorSlots, not FeedbackVectorSlots.
164 // The reason is that FeedbackVectorSlots need a full code environment
165 // to fully test (See VectorICProfilerStatistics test below).
166 FeedbackVectorSpec spec(zone);
167 for (int i = 0; i < 5; i++) {
168 spec.AddGeneralSlot();
169 }
170 Handle<TypeFeedbackVector> vector = NewTypeFeedbackVector(isolate, &spec);
171 FeedbackVectorHelper helper(vector);
172
173 // Fill with information
174 vector->Set(helper.slot(0), Smi::FromInt(1));
175 Handle<WeakCell> cell = factory->NewWeakCell(factory->fixed_array_map());
176 vector->Set(helper.slot(1), *cell);
177 Handle<AllocationSite> site = factory->NewAllocationSite();
178 vector->Set(helper.slot(2), *site);
179
180 // GC time clearing leaves slots alone.
181 vector->ClearSlotsAtGCTime(NULL);
182 Object* obj = vector->Get(helper.slot(1));
183 CHECK(obj->IsWeakCell() && !WeakCell::cast(obj)->cleared());
184
185 vector->ClearSlots(NULL);
186
187 // The feedback vector slots are cleared. AllocationSites are still granted
188 // an exemption from clearing, as are smis.
189 CHECK_EQ(Smi::FromInt(1), vector->Get(helper.slot(0)));
190 CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),
191 vector->Get(helper.slot(1)));
192 CHECK(vector->Get(helper.slot(2))->IsAllocationSite());
193 }
194
195
TEST(VectorCallICStates)196 TEST(VectorCallICStates) {
197 if (i::FLAG_always_opt) return;
198 CcTest::InitializeVM();
199 LocalContext context;
200 v8::HandleScope scope(context->GetIsolate());
201 Isolate* isolate = CcTest::i_isolate();
202 Heap* heap = isolate->heap();
203
204 // Make sure function f has a call that uses a type feedback slot.
205 CompileRun(
206 "function foo() { return 17; }"
207 "function f(a) { a(); } f(foo);");
208 Handle<JSFunction> f = GetFunction("f");
209 // There should be one IC.
210 Handle<TypeFeedbackVector> feedback_vector =
211 Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
212 FeedbackVectorSlot slot(0);
213 CallICNexus nexus(feedback_vector, slot);
214 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
215 // CallIC doesn't return map feedback.
216 CHECK(!nexus.FindFirstMap());
217
218 CompileRun("f(function() { return 16; })");
219 CHECK_EQ(GENERIC, nexus.StateFromFeedback());
220
221 // After a collection, state should remain GENERIC.
222 heap->CollectAllGarbage();
223 CHECK_EQ(GENERIC, nexus.StateFromFeedback());
224
225 // A call to Array is special, it contains an AllocationSite as feedback.
226 // Clear the IC manually in order to test this case.
227 nexus.Clear(f->shared()->code());
228 CompileRun("f(Array)");
229 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
230 CHECK(nexus.GetFeedback()->IsAllocationSite());
231
232 heap->CollectAllGarbage();
233 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
234 }
235
TEST(VectorCallCounts)236 TEST(VectorCallCounts) {
237 if (i::FLAG_always_opt) return;
238 CcTest::InitializeVM();
239 LocalContext context;
240 v8::HandleScope scope(context->GetIsolate());
241 Isolate* isolate = CcTest::i_isolate();
242
243 // Make sure function f has a call that uses a type feedback slot.
244 CompileRun(
245 "function foo() { return 17; }"
246 "function f(a) { a(); } f(foo);");
247 Handle<JSFunction> f = GetFunction("f");
248 // There should be one IC.
249 Handle<TypeFeedbackVector> feedback_vector =
250 Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
251 FeedbackVectorSlot slot(0);
252 CallICNexus nexus(feedback_vector, slot);
253 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
254
255 CompileRun("f(foo); f(foo);");
256 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
257 CHECK_EQ(3, nexus.ExtractCallCount());
258
259 CompileRun(
260 "function Foo() {}"
261 "function f(a) { new a(); } f(Foo);");
262 f = GetFunction("f");
263 // There should be one IC.
264 feedback_vector = Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
265 FeedbackVectorSlot cslot(1);
266
267 CompileRun("f(Foo); f(Foo);");
268 CHECK(feedback_vector->Get(cslot)->IsSmi());
269 CHECK_EQ(3, Smi::cast(feedback_vector->Get(cslot))->value());
270 }
271
TEST(VectorLoadICStates)272 TEST(VectorLoadICStates) {
273 if (i::FLAG_always_opt) return;
274 CcTest::InitializeVM();
275 LocalContext context;
276 v8::HandleScope scope(context->GetIsolate());
277 Isolate* isolate = CcTest::i_isolate();
278 Heap* heap = isolate->heap();
279
280 // Make sure function f has a call that uses a type feedback slot.
281 CompileRun(
282 "var o = { foo: 3 };"
283 "function f(a) { return a.foo; } f(o);");
284 Handle<JSFunction> f = GetFunction("f");
285 // There should be one IC.
286 Handle<TypeFeedbackVector> feedback_vector =
287 Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
288 FeedbackVectorSlot slot(0);
289 LoadICNexus nexus(feedback_vector, slot);
290 CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
291
292 CompileRun("f(o)");
293 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
294 // Verify that the monomorphic map is the one we expect.
295 v8::MaybeLocal<v8::Value> v8_o =
296 CcTest::global()->Get(context.local(), v8_str("o"));
297 Handle<JSObject> o =
298 Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
299 CHECK_EQ(o->map(), nexus.FindFirstMap());
300
301 // Now go polymorphic.
302 CompileRun("f({ blarg: 3, foo: 2 })");
303 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
304
305 CompileRun(
306 "delete o.foo;"
307 "f(o)");
308 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
309
310 CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
311 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
312 MapHandleList maps;
313 nexus.FindAllMaps(&maps);
314 CHECK_EQ(4, maps.length());
315
316 // Finally driven megamorphic.
317 CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
318 CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
319 CHECK(!nexus.FindFirstMap());
320
321 // After a collection, state should not be reset to PREMONOMORPHIC.
322 heap->CollectAllGarbage();
323 CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
324 }
325
326
TEST(VectorLoadICSlotSharing)327 TEST(VectorLoadICSlotSharing) {
328 if (i::FLAG_always_opt) return;
329 CcTest::InitializeVM();
330 LocalContext context;
331 v8::HandleScope scope(context->GetIsolate());
332 Isolate* isolate = CcTest::i_isolate();
333
334 // Function f has 3 LoadICs, one for each o, but the ICs share the same
335 // feedback vector IC slot.
336 CompileRun(
337 "o = 10;"
338 "function f() {"
339 " var x = o + 10;"
340 " return o + x + o;"
341 "}"
342 "f();");
343 Handle<JSFunction> f = GetFunction("f");
344 // There should be one IC slot.
345 Handle<TypeFeedbackVector> feedback_vector =
346 Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
347 FeedbackVectorHelper helper(feedback_vector);
348 CHECK_EQ(1, helper.slot_count());
349 FeedbackVectorSlot slot(0);
350 LoadGlobalICNexus nexus(feedback_vector, slot);
351 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
352 }
353
354
TEST(VectorLoadICOnSmi)355 TEST(VectorLoadICOnSmi) {
356 if (i::FLAG_always_opt) return;
357 CcTest::InitializeVM();
358 LocalContext context;
359 v8::HandleScope scope(context->GetIsolate());
360 Isolate* isolate = CcTest::i_isolate();
361 Heap* heap = isolate->heap();
362
363 // Make sure function f has a call that uses a type feedback slot.
364 CompileRun(
365 "var o = { foo: 3 };"
366 "function f(a) { return a.foo; } f(o);");
367 Handle<JSFunction> f = GetFunction("f");
368 // There should be one IC.
369 Handle<TypeFeedbackVector> feedback_vector =
370 Handle<TypeFeedbackVector>(f->feedback_vector(), isolate);
371 FeedbackVectorSlot slot(0);
372 LoadICNexus nexus(feedback_vector, slot);
373 CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
374
375 CompileRun("f(34)");
376 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
377 // Verify that the monomorphic map is the one we expect.
378 Map* number_map = heap->heap_number_map();
379 CHECK_EQ(number_map, nexus.FindFirstMap());
380
381 // Now go polymorphic on o.
382 CompileRun("f(o)");
383 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
384
385 MapHandleList maps;
386 nexus.FindAllMaps(&maps);
387 CHECK_EQ(2, maps.length());
388
389 // One of the maps should be the o map.
390 v8::MaybeLocal<v8::Value> v8_o =
391 CcTest::global()->Get(context.local(), v8_str("o"));
392 Handle<JSObject> o =
393 Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
394 bool number_map_found = false;
395 bool o_map_found = false;
396 for (int i = 0; i < maps.length(); i++) {
397 Handle<Map> current = maps[i];
398 if (*current == number_map)
399 number_map_found = true;
400 else if (*current == o->map())
401 o_map_found = true;
402 }
403 CHECK(number_map_found && o_map_found);
404
405 // The degree of polymorphism doesn't change.
406 CompileRun("f(100)");
407 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
408 MapHandleList maps2;
409 nexus.FindAllMaps(&maps2);
410 CHECK_EQ(2, maps2.length());
411 }
412
413
TEST(ReferenceContextAllocatesNoSlots)414 TEST(ReferenceContextAllocatesNoSlots) {
415 if (i::FLAG_always_opt) return;
416 CcTest::InitializeVM();
417 LocalContext context;
418 v8::HandleScope scope(context->GetIsolate());
419 Isolate* isolate = CcTest::i_isolate();
420
421 {
422 CompileRun(
423 "function testvar(x) {"
424 " y = x;"
425 " y = a;"
426 " return y;"
427 "}"
428 "a = 3;"
429 "testvar({});");
430
431 Handle<JSFunction> f = GetFunction("testvar");
432
433 // There should be two LOAD_ICs, one for a and one for y at the end.
434 Handle<TypeFeedbackVector> feedback_vector =
435 handle(f->feedback_vector(), isolate);
436 FeedbackVectorHelper helper(feedback_vector);
437 CHECK_EQ(4, helper.slot_count());
438 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::STORE_IC);
439 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
440 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
441 CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
442 }
443
444 {
445 CompileRun(
446 "function testprop(x) {"
447 " x.blue = a;"
448 "}"
449 "testprop({ blue: 3 });");
450
451 Handle<JSFunction> f = GetFunction("testprop");
452
453 // There should be one LOAD_IC, for the load of a.
454 Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
455 FeedbackVectorHelper helper(feedback_vector);
456 CHECK_EQ(2, helper.slot_count());
457 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
458 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::STORE_IC);
459 }
460
461 {
462 CompileRun(
463 "function testpropfunc(x) {"
464 " x().blue = a;"
465 " return x().blue;"
466 "}"
467 "function makeresult() { return { blue: 3 }; }"
468 "testpropfunc(makeresult);");
469
470 Handle<JSFunction> f = GetFunction("testpropfunc");
471
472 // There should be 1 LOAD_GLOBAL_IC to load x (in both cases), 2 CALL_ICs
473 // to call x and a LOAD_IC to load blue.
474 Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
475 FeedbackVectorHelper helper(feedback_vector);
476 CHECK_EQ(5, helper.slot_count());
477 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::CALL_IC);
478 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
479 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
480 CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::CALL_IC);
481 CHECK_SLOT_KIND(helper, 4, FeedbackVectorSlotKind::LOAD_IC);
482 }
483
484 {
485 CompileRun(
486 "function testkeyedprop(x) {"
487 " x[0] = a;"
488 " return x[0];"
489 "}"
490 "testkeyedprop([0, 1, 2]);");
491
492 Handle<JSFunction> f = GetFunction("testkeyedprop");
493
494 // There should be 1 LOAD_GLOBAL_ICs for the load of a, and one
495 // KEYED_LOAD_IC for the load of x[0] in the return statement.
496 Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
497 FeedbackVectorHelper helper(feedback_vector);
498 CHECK_EQ(3, helper.slot_count());
499 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
500 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::KEYED_STORE_IC);
501 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::KEYED_LOAD_IC);
502 }
503
504 {
505 CompileRun(
506 "function testcompound(x) {"
507 " x.old = x.young = x.in_between = a;"
508 " return x.old + x.young;"
509 "}"
510 "testcompound({ old: 3, young: 3, in_between: 3 });");
511
512 Handle<JSFunction> f = GetFunction("testcompound");
513
514 // There should be 1 LOAD_GLOBAL_IC for load of a and 2 LOAD_ICs, for load
515 // of x.old and x.young.
516 Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
517 FeedbackVectorHelper helper(feedback_vector);
518 CHECK_EQ(6, helper.slot_count());
519 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
520 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::STORE_IC);
521 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
522 CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::STORE_IC);
523 CHECK_SLOT_KIND(helper, 4, FeedbackVectorSlotKind::LOAD_IC);
524 CHECK_SLOT_KIND(helper, 5, FeedbackVectorSlotKind::LOAD_IC);
525 }
526 }
527
528
TEST(VectorStoreICBasic)529 TEST(VectorStoreICBasic) {
530 if (i::FLAG_always_opt) return;
531
532 CcTest::InitializeVM();
533 LocalContext context;
534 v8::HandleScope scope(context->GetIsolate());
535
536 CompileRun(
537 "function f(a) {"
538 " a.foo = 5;"
539 "}"
540 "var a = { foo: 3 };"
541 "f(a);"
542 "f(a);"
543 "f(a);");
544 Handle<JSFunction> f = GetFunction("f");
545 // There should be one IC slot.
546 Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
547 FeedbackVectorHelper helper(feedback_vector);
548 CHECK_EQ(1, helper.slot_count());
549 FeedbackVectorSlot slot(0);
550 StoreICNexus nexus(feedback_vector, slot);
551 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
552 }
553
554 } // namespace
555