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->shared()->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
236
TEST(VectorLoadICStates)237 TEST(VectorLoadICStates) {
238 if (i::FLAG_always_opt) return;
239 CcTest::InitializeVM();
240 LocalContext context;
241 v8::HandleScope scope(context->GetIsolate());
242 Isolate* isolate = CcTest::i_isolate();
243 Heap* heap = isolate->heap();
244
245 // Make sure function f has a call that uses a type feedback slot.
246 CompileRun(
247 "var o = { foo: 3 };"
248 "function f(a) { return a.foo; } f(o);");
249 Handle<JSFunction> f = GetFunction("f");
250 // There should be one IC.
251 Handle<TypeFeedbackVector> feedback_vector =
252 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
253 FeedbackVectorSlot slot(0);
254 LoadICNexus nexus(feedback_vector, slot);
255 CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
256
257 CompileRun("f(o)");
258 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
259 // Verify that the monomorphic map is the one we expect.
260 v8::MaybeLocal<v8::Value> v8_o =
261 CcTest::global()->Get(context.local(), v8_str("o"));
262 Handle<JSObject> o =
263 Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
264 CHECK_EQ(o->map(), nexus.FindFirstMap());
265
266 // Now go polymorphic.
267 CompileRun("f({ blarg: 3, foo: 2 })");
268 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
269
270 CompileRun(
271 "delete o.foo;"
272 "f(o)");
273 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
274
275 CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
276 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
277 MapHandleList maps;
278 nexus.FindAllMaps(&maps);
279 CHECK_EQ(4, maps.length());
280
281 // Finally driven megamorphic.
282 CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
283 CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
284 CHECK(!nexus.FindFirstMap());
285
286 // After a collection, state should not be reset to PREMONOMORPHIC.
287 heap->CollectAllGarbage();
288 CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
289 }
290
291
TEST(VectorLoadICSlotSharing)292 TEST(VectorLoadICSlotSharing) {
293 if (i::FLAG_always_opt) return;
294 CcTest::InitializeVM();
295 LocalContext context;
296 v8::HandleScope scope(context->GetIsolate());
297 Isolate* isolate = CcTest::i_isolate();
298
299 // Function f has 3 LoadICs, one for each o, but the ICs share the same
300 // feedback vector IC slot.
301 CompileRun(
302 "o = 10;"
303 "function f() {"
304 " var x = o + 10;"
305 " return o + x + o;"
306 "}"
307 "f();");
308 Handle<JSFunction> f = GetFunction("f");
309 // There should be one IC slot.
310 Handle<TypeFeedbackVector> feedback_vector =
311 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
312 FeedbackVectorHelper helper(feedback_vector);
313 CHECK_EQ(1, helper.slot_count());
314 FeedbackVectorSlot slot(0);
315 LoadICNexus nexus(feedback_vector, slot);
316 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
317 }
318
319
TEST(VectorLoadICOnSmi)320 TEST(VectorLoadICOnSmi) {
321 if (i::FLAG_always_opt) return;
322 CcTest::InitializeVM();
323 LocalContext context;
324 v8::HandleScope scope(context->GetIsolate());
325 Isolate* isolate = CcTest::i_isolate();
326 Heap* heap = isolate->heap();
327
328 // Make sure function f has a call that uses a type feedback slot.
329 CompileRun(
330 "var o = { foo: 3 };"
331 "function f(a) { return a.foo; } f(o);");
332 Handle<JSFunction> f = GetFunction("f");
333 // There should be one IC.
334 Handle<TypeFeedbackVector> feedback_vector =
335 Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
336 FeedbackVectorSlot slot(0);
337 LoadICNexus nexus(feedback_vector, slot);
338 CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
339
340 CompileRun("f(34)");
341 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
342 // Verify that the monomorphic map is the one we expect.
343 Map* number_map = heap->heap_number_map();
344 CHECK_EQ(number_map, nexus.FindFirstMap());
345
346 // Now go polymorphic on o.
347 CompileRun("f(o)");
348 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
349
350 MapHandleList maps;
351 nexus.FindAllMaps(&maps);
352 CHECK_EQ(2, maps.length());
353
354 // One of the maps should be the o map.
355 v8::MaybeLocal<v8::Value> v8_o =
356 CcTest::global()->Get(context.local(), v8_str("o"));
357 Handle<JSObject> o =
358 Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
359 bool number_map_found = false;
360 bool o_map_found = false;
361 for (int i = 0; i < maps.length(); i++) {
362 Handle<Map> current = maps[i];
363 if (*current == number_map)
364 number_map_found = true;
365 else if (*current == o->map())
366 o_map_found = true;
367 }
368 CHECK(number_map_found && o_map_found);
369
370 // The degree of polymorphism doesn't change.
371 CompileRun("f(100)");
372 CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
373 MapHandleList maps2;
374 nexus.FindAllMaps(&maps2);
375 CHECK_EQ(2, maps2.length());
376 }
377
378
TEST(ReferenceContextAllocatesNoSlots)379 TEST(ReferenceContextAllocatesNoSlots) {
380 if (i::FLAG_always_opt) return;
381 CcTest::InitializeVM();
382 LocalContext context;
383 v8::HandleScope scope(context->GetIsolate());
384 Isolate* isolate = CcTest::i_isolate();
385
386 {
387 CompileRun(
388 "function testvar(x) {"
389 " y = x;"
390 " y = a;"
391 " return y;"
392 "}"
393 "a = 3;"
394 "testvar({});");
395
396 Handle<JSFunction> f = GetFunction("testvar");
397
398 // There should be two LOAD_ICs, one for a and one for y at the end.
399 Handle<TypeFeedbackVector> feedback_vector =
400 handle(f->shared()->feedback_vector(), isolate);
401 FeedbackVectorHelper helper(feedback_vector);
402 CHECK_EQ(4, helper.slot_count());
403 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::STORE_IC);
404 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::LOAD_IC);
405 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
406 CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::LOAD_IC);
407 }
408
409 {
410 CompileRun(
411 "function testprop(x) {"
412 " x.blue = a;"
413 "}"
414 "testprop({ blue: 3 });");
415
416 Handle<JSFunction> f = GetFunction("testprop");
417
418 // There should be one LOAD_IC, for the load of a.
419 Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
420 FeedbackVectorHelper helper(feedback_vector);
421 CHECK_EQ(2, helper.slot_count());
422 }
423
424 {
425 CompileRun(
426 "function testpropfunc(x) {"
427 " x().blue = a;"
428 " return x().blue;"
429 "}"
430 "function makeresult() { return { blue: 3 }; }"
431 "testpropfunc(makeresult);");
432
433 Handle<JSFunction> f = GetFunction("testpropfunc");
434
435 // There should be 2 LOAD_ICs and 2 CALL_ICs.
436 Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
437 FeedbackVectorHelper helper(feedback_vector);
438 CHECK_EQ(5, helper.slot_count());
439 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::CALL_IC);
440 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::LOAD_IC);
441 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
442 CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::CALL_IC);
443 CHECK_SLOT_KIND(helper, 4, FeedbackVectorSlotKind::LOAD_IC);
444 }
445
446 {
447 CompileRun(
448 "function testkeyedprop(x) {"
449 " x[0] = a;"
450 " return x[0];"
451 "}"
452 "testkeyedprop([0, 1, 2]);");
453
454 Handle<JSFunction> f = GetFunction("testkeyedprop");
455
456 // There should be 1 LOAD_ICs for the load of a, and one KEYED_LOAD_IC for
457 // the load of x[0] in the return statement.
458 Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
459 FeedbackVectorHelper helper(feedback_vector);
460 CHECK_EQ(3, helper.slot_count());
461 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_IC);
462 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::KEYED_STORE_IC);
463 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::KEYED_LOAD_IC);
464 }
465
466 {
467 CompileRun(
468 "function testcompound(x) {"
469 " x.old = x.young = x.in_between = a;"
470 " return x.old + x.young;"
471 "}"
472 "testcompound({ old: 3, young: 3, in_between: 3 });");
473
474 Handle<JSFunction> f = GetFunction("testcompound");
475
476 // There should be 3 LOAD_ICs, for load of a and load of x.old and x.young.
477 Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
478 FeedbackVectorHelper helper(feedback_vector);
479 CHECK_EQ(6, helper.slot_count());
480 CHECK_SLOT_KIND(helper, 0, FeedbackVectorSlotKind::LOAD_IC);
481 CHECK_SLOT_KIND(helper, 1, FeedbackVectorSlotKind::STORE_IC);
482 CHECK_SLOT_KIND(helper, 2, FeedbackVectorSlotKind::STORE_IC);
483 CHECK_SLOT_KIND(helper, 3, FeedbackVectorSlotKind::STORE_IC);
484 CHECK_SLOT_KIND(helper, 4, FeedbackVectorSlotKind::LOAD_IC);
485 CHECK_SLOT_KIND(helper, 5, FeedbackVectorSlotKind::LOAD_IC);
486 }
487 }
488
489
TEST(VectorStoreICBasic)490 TEST(VectorStoreICBasic) {
491 if (i::FLAG_always_opt) return;
492
493 CcTest::InitializeVM();
494 LocalContext context;
495 v8::HandleScope scope(context->GetIsolate());
496
497 CompileRun(
498 "function f(a) {"
499 " a.foo = 5;"
500 "}"
501 "var a = { foo: 3 };"
502 "f(a);"
503 "f(a);"
504 "f(a);");
505 Handle<JSFunction> f = GetFunction("f");
506 // There should be one IC slot.
507 Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
508 FeedbackVectorHelper helper(feedback_vector);
509 CHECK_EQ(1, helper.slot_count());
510 FeedbackVectorSlot slot(0);
511 StoreICNexus nexus(feedback_vector, slot);
512 CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
513 }
514
515 } // namespace
516