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/init/v8.h"
6 #include "test/cctest/cctest.h"
7
8 #include "src/api/api-inl.h"
9 #include "src/codegen/macro-assembler.h"
10 #include "src/debug/debug.h"
11 #include "src/execution/execution.h"
12 #include "src/handles/global-handles.h"
13 #include "src/heap/factory.h"
14 #include "src/objects/feedback-cell-inl.h"
15 #include "src/objects/objects-inl.h"
16 #include "test/cctest/test-feedback-vector.h"
17
18 namespace v8 {
19 namespace internal {
20
21 namespace {
22
23 #define CHECK_SLOT_KIND(helper, index, expected_kind) \
24 CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index)));
25
26
GetFunction(const char * name)27 static Handle<JSFunction> GetFunction(const char* name) {
28 v8::MaybeLocal<v8::Value> v8_f = CcTest::global()->Get(
29 CcTest::isolate()->GetCurrentContext(), v8_str(name));
30 Handle<JSFunction> f =
31 Handle<JSFunction>::cast(v8::Utils::OpenHandle(*v8_f.ToLocalChecked()));
32 return f;
33 }
34
35
TEST(VectorStructure)36 TEST(VectorStructure) {
37 LocalContext context;
38 v8::HandleScope scope(context->GetIsolate());
39 Isolate* isolate = CcTest::i_isolate();
40 Factory* factory = isolate->factory();
41 Zone zone(isolate->allocator(), ZONE_NAME);
42
43 Handle<FeedbackVector> vector;
44
45 {
46 FeedbackVectorSpec one_slot(&zone);
47 one_slot.AddForInSlot();
48 vector = NewFeedbackVector(isolate, &one_slot);
49 FeedbackVectorHelper helper(vector);
50 CHECK_EQ(1, helper.slot_count());
51 }
52
53 {
54 FeedbackVectorSpec one_icslot(&zone);
55 one_icslot.AddCallICSlot();
56 vector = NewFeedbackVector(isolate, &one_icslot);
57 FeedbackVectorHelper helper(vector);
58 CHECK_EQ(1, helper.slot_count());
59 }
60
61 {
62 FeedbackVectorSpec spec(&zone);
63 for (int i = 0; i < 3; i++) {
64 spec.AddForInSlot();
65 }
66 for (int i = 0; i < 5; i++) {
67 spec.AddCallICSlot();
68 }
69 vector = NewFeedbackVector(isolate, &spec);
70 FeedbackVectorHelper helper(vector);
71 CHECK_EQ(8, helper.slot_count());
72
73 int index = vector->GetIndex(helper.slot(0));
74
75 CHECK_EQ(helper.slot(0), vector->ToSlot(index));
76
77 index = vector->GetIndex(helper.slot(3));
78 CHECK_EQ(helper.slot(3), vector->ToSlot(index));
79
80 index = vector->GetIndex(helper.slot(7));
81 CHECK_EQ(3 + 4 * FeedbackMetadata::GetSlotSize(FeedbackSlotKind::kCall),
82 index);
83 CHECK_EQ(helper.slot(7), vector->ToSlot(index));
84
85 CHECK_EQ(3 + 5 * FeedbackMetadata::GetSlotSize(FeedbackSlotKind::kCall),
86 vector->length());
87 }
88
89 {
90 FeedbackVectorSpec spec(&zone);
91 spec.AddForInSlot();
92 spec.AddCreateClosureSlot();
93 spec.AddForInSlot();
94 vector = NewFeedbackVector(isolate, &spec);
95 FeedbackVectorHelper helper(vector);
96 FeedbackCell cell = *vector->GetClosureFeedbackCell(0);
97 CHECK_EQ(cell.value(), *factory->undefined_value());
98 }
99 }
100
101
102 // IC slots need an encoding to recognize what is in there.
TEST(VectorICMetadata)103 TEST(VectorICMetadata) {
104 LocalContext context;
105 v8::HandleScope scope(context->GetIsolate());
106 Isolate* isolate = CcTest::i_isolate();
107 Zone zone(isolate->allocator(), ZONE_NAME);
108
109 FeedbackVectorSpec spec(&zone);
110 // Set metadata.
111 for (int i = 0; i < 40; i++) {
112 switch (i % 4) {
113 case 0:
114 spec.AddForInSlot();
115 break;
116 case 1:
117 spec.AddCallICSlot();
118 break;
119 case 2:
120 spec.AddLoadICSlot();
121 break;
122 case 3:
123 spec.AddKeyedLoadICSlot();
124 break;
125 }
126 }
127
128 Handle<FeedbackVector> vector = NewFeedbackVector(isolate, &spec);
129 FeedbackVectorHelper helper(vector);
130 CHECK_EQ(40, helper.slot_count());
131
132 // Meanwhile set some feedback values and type feedback values to
133 // verify the data structure remains intact.
134 vector->SynchronizedSet(FeedbackSlot(0), MaybeObject::FromObject(*vector));
135
136 // Verify the metadata is correctly set up from the spec.
137 for (int i = 0; i < 40; i++) {
138 FeedbackSlotKind kind = vector->GetKind(helper.slot(i));
139 switch (i % 4) {
140 case 0:
141 CHECK_EQ(FeedbackSlotKind::kForIn, kind);
142 break;
143 case 1:
144 CHECK_EQ(FeedbackSlotKind::kCall, kind);
145 break;
146 case 2:
147 CHECK_EQ(FeedbackSlotKind::kLoadProperty, kind);
148 break;
149 case 3:
150 CHECK_EQ(FeedbackSlotKind::kLoadKeyed, kind);
151 break;
152 }
153 }
154 }
155
156
TEST(VectorCallICStates)157 TEST(VectorCallICStates) {
158 if (!i::FLAG_use_ic) return;
159 if (i::FLAG_always_opt) return;
160 FLAG_allow_natives_syntax = true;
161
162 CcTest::InitializeVM();
163 LocalContext context;
164 v8::HandleScope scope(context->GetIsolate());
165 Isolate* isolate = CcTest::i_isolate();
166 // Make sure function f has a call that uses a type feedback slot.
167 CompileRun(
168 "function foo() { return 17; };"
169 "%EnsureFeedbackVectorForFunction(f);"
170 "function f(a) { a(); } f(foo);");
171 Handle<JSFunction> f = GetFunction("f");
172 // There should be one IC.
173 Handle<FeedbackVector> feedback_vector =
174 Handle<FeedbackVector>(f->feedback_vector(), isolate);
175 FeedbackSlot slot(0);
176 FeedbackNexus nexus(feedback_vector, slot);
177 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
178
179 CompileRun("f(function() { return 16; })");
180 CHECK_EQ(GENERIC, nexus.ic_state());
181
182 // After a collection, state should remain GENERIC.
183 CcTest::CollectAllGarbage();
184 CHECK_EQ(GENERIC, nexus.ic_state());
185 }
186
187 // Test the Call IC states transfer with Function.prototype.apply
TEST(VectorCallICStateApply)188 TEST(VectorCallICStateApply) {
189 if (!i::FLAG_use_ic) return;
190 if (i::FLAG_always_opt) return;
191 FLAG_allow_natives_syntax = true;
192
193 CcTest::InitializeVM();
194 LocalContext context;
195 v8::HandleScope scope(context->GetIsolate());
196 Isolate* isolate = CcTest::i_isolate();
197 // Make sure function f has a call that uses a type feedback slot.
198 CompileRun(
199 "var F;"
200 "%EnsureFeedbackVectorForFunction(foo);"
201 "function foo() { return F.apply(null, arguments); }"
202 "F = Math.min;"
203 "foo();");
204 Handle<JSFunction> foo = GetFunction("foo");
205 Handle<JSFunction> F = GetFunction("F");
206 Handle<FeedbackVector> feedback_vector =
207 Handle<FeedbackVector>(foo->feedback_vector(), isolate);
208 FeedbackSlot slot(4);
209 FeedbackNexus nexus(feedback_vector, slot);
210 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
211 CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent());
212 HeapObject heap_object;
213 CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
214 CHECK_EQ(*F, heap_object);
215
216 CompileRun(
217 "F = Math.max;"
218 "foo();");
219 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
220 CHECK_EQ(CallFeedbackContent::kTarget, nexus.GetCallFeedbackContent());
221 CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
222 CHECK_EQ(*isolate->function_prototype_apply(), heap_object);
223
224 CompileRun(
225 "F.apply = (function () { return; });"
226 "foo();");
227 CHECK_EQ(GENERIC, nexus.ic_state());
228 }
229
TEST(VectorCallFeedback)230 TEST(VectorCallFeedback) {
231 if (!i::FLAG_use_ic) return;
232 if (i::FLAG_always_opt) return;
233 FLAG_allow_natives_syntax = true;
234
235 CcTest::InitializeVM();
236 LocalContext context;
237 v8::HandleScope scope(context->GetIsolate());
238 Isolate* isolate = CcTest::i_isolate();
239 // Make sure function f has a call that uses a type feedback slot.
240 CompileRun(
241 "function foo() { return 17; }"
242 "%EnsureFeedbackVectorForFunction(f);"
243 "function f(a) { a(); } f(foo);");
244 Handle<JSFunction> f = GetFunction("f");
245 Handle<JSFunction> foo = GetFunction("foo");
246 // There should be one IC.
247 Handle<FeedbackVector> feedback_vector =
248 Handle<FeedbackVector>(f->feedback_vector(), isolate);
249 FeedbackSlot slot(0);
250 FeedbackNexus nexus(feedback_vector, slot);
251
252 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
253 HeapObject heap_object;
254 CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
255 CHECK_EQ(*foo, heap_object);
256
257 CcTest::CollectAllGarbage();
258 // It should stay monomorphic even after a GC.
259 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
260 }
261
TEST(VectorPolymorphicCallFeedback)262 TEST(VectorPolymorphicCallFeedback) {
263 if (!i::FLAG_use_ic) return;
264 if (i::FLAG_always_opt) return;
265 FLAG_allow_natives_syntax = true;
266 FLAG_lazy_feedback_allocation = false;
267
268 CcTest::InitializeVM();
269 LocalContext context;
270 v8::HandleScope scope(context->GetIsolate());
271 Isolate* isolate = CcTest::i_isolate();
272 // Make sure the call feedback of a() in f() becomes polymorphic.
273 CompileRun(
274 "function foo_maker() { return () => { return 17; } }"
275 "a_foo = foo_maker();"
276 "function f(a) { a(); } f(foo_maker());"
277 "f(foo_maker());");
278 Handle<JSFunction> f = GetFunction("f");
279 Handle<JSFunction> a_foo = GetFunction("a_foo");
280 // There should be one IC.
281 Handle<FeedbackVector> feedback_vector =
282 Handle<FeedbackVector>(f->feedback_vector(), isolate);
283 FeedbackSlot slot(0);
284 FeedbackNexus nexus(feedback_vector, slot);
285
286 CHECK_EQ(POLYMORPHIC, nexus.ic_state());
287 HeapObject heap_object;
288 CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
289 CHECK(heap_object.IsFeedbackCell(isolate));
290 // Ensure this is the feedback cell for the closure returned by
291 // foo_maker.
292 CHECK_EQ(heap_object, a_foo->raw_feedback_cell());
293 }
294
TEST(VectorCallFeedbackForArray)295 TEST(VectorCallFeedbackForArray) {
296 if (!i::FLAG_use_ic) return;
297 if (i::FLAG_always_opt) return;
298 FLAG_allow_natives_syntax = true;
299
300 CcTest::InitializeVM();
301 LocalContext context;
302 v8::HandleScope scope(context->GetIsolate());
303 Isolate* isolate = CcTest::i_isolate();
304 // Make sure function f has a call that uses a type feedback slot.
305 CompileRun(
306 "function f(a) { a(); };"
307 "%EnsureFeedbackVectorForFunction(f);"
308 "f(Array);");
309 Handle<JSFunction> f = GetFunction("f");
310 // There should be one IC.
311 Handle<FeedbackVector> feedback_vector =
312 Handle<FeedbackVector>(f->feedback_vector(), isolate);
313 FeedbackSlot slot(0);
314 FeedbackNexus nexus(feedback_vector, slot);
315
316 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
317 HeapObject heap_object;
318 CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
319 CHECK_EQ(*isolate->array_function(), heap_object);
320
321 CcTest::CollectAllGarbage();
322 // It should stay monomorphic even after a GC.
323 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
324 }
325
TEST(VectorCallCounts)326 TEST(VectorCallCounts) {
327 if (!i::FLAG_use_ic) return;
328 if (i::FLAG_always_opt) return;
329 FLAG_allow_natives_syntax = true;
330
331 CcTest::InitializeVM();
332 LocalContext context;
333 v8::HandleScope scope(context->GetIsolate());
334 Isolate* isolate = CcTest::i_isolate();
335
336 // Make sure function f has a call that uses a type feedback slot.
337 CompileRun(
338 "function foo() { return 17; }"
339 "%EnsureFeedbackVectorForFunction(f);"
340 "function f(a) { a(); } f(foo);");
341 Handle<JSFunction> f = GetFunction("f");
342 // There should be one IC.
343 Handle<FeedbackVector> feedback_vector =
344 Handle<FeedbackVector>(f->feedback_vector(), isolate);
345 FeedbackSlot slot(0);
346 FeedbackNexus nexus(feedback_vector, slot);
347 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
348
349 CompileRun("f(foo); f(foo);");
350 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
351 CHECK_EQ(3, nexus.GetCallCount());
352
353 // Send the IC megamorphic, but we should still have incrementing counts.
354 CompileRun("f(function() { return 12; });");
355 CHECK_EQ(GENERIC, nexus.ic_state());
356 CHECK_EQ(4, nexus.GetCallCount());
357 }
358
TEST(VectorConstructCounts)359 TEST(VectorConstructCounts) {
360 if (!i::FLAG_use_ic) return;
361 if (i::FLAG_always_opt) return;
362 FLAG_allow_natives_syntax = true;
363
364 CcTest::InitializeVM();
365 LocalContext context;
366 v8::HandleScope scope(context->GetIsolate());
367 Isolate* isolate = CcTest::i_isolate();
368
369 // Make sure function f has a call that uses a type feedback slot.
370 CompileRun(
371 "function Foo() {}"
372 "%EnsureFeedbackVectorForFunction(f);"
373 "function f(a) { new a(); } f(Foo);");
374 Handle<JSFunction> f = GetFunction("f");
375 Handle<FeedbackVector> feedback_vector =
376 Handle<FeedbackVector>(f->feedback_vector(), isolate);
377
378 FeedbackSlot slot(0);
379 FeedbackNexus nexus(feedback_vector, slot);
380 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
381
382 CHECK(feedback_vector->Get(slot)->IsWeak());
383
384 CompileRun("f(Foo); f(Foo);");
385 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
386 CHECK_EQ(3, nexus.GetCallCount());
387
388 // Send the IC megamorphic, but we should still have incrementing counts.
389 CompileRun("f(function() {});");
390 CHECK_EQ(GENERIC, nexus.ic_state());
391 CHECK_EQ(4, nexus.GetCallCount());
392 }
393
TEST(VectorSpeculationMode)394 TEST(VectorSpeculationMode) {
395 if (!i::FLAG_use_ic) return;
396 if (i::FLAG_always_opt) return;
397 FLAG_allow_natives_syntax = true;
398
399 CcTest::InitializeVM();
400 LocalContext context;
401 v8::HandleScope scope(context->GetIsolate());
402 Isolate* isolate = CcTest::i_isolate();
403
404 // Make sure function f has a call that uses a type feedback slot.
405 CompileRun(
406 "function Foo() {}"
407 "%EnsureFeedbackVectorForFunction(f);"
408 "function f(a) { new a(); } f(Foo);");
409 Handle<JSFunction> f = GetFunction("f");
410 Handle<FeedbackVector> feedback_vector =
411 Handle<FeedbackVector>(f->feedback_vector(), isolate);
412
413 FeedbackSlot slot(0);
414 FeedbackNexus nexus(feedback_vector, slot);
415 CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
416
417 CompileRun("f(Foo); f(Foo);");
418 CHECK_EQ(3, nexus.GetCallCount());
419 CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
420
421 nexus.SetSpeculationMode(SpeculationMode::kDisallowSpeculation);
422 CHECK_EQ(SpeculationMode::kDisallowSpeculation, nexus.GetSpeculationMode());
423 CHECK_EQ(3, nexus.GetCallCount());
424
425 nexus.SetSpeculationMode(SpeculationMode::kAllowSpeculation);
426 CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
427 CHECK_EQ(3, nexus.GetCallCount());
428 }
429
TEST(VectorCallSpeculationModeAndFeedbackContent)430 TEST(VectorCallSpeculationModeAndFeedbackContent) {
431 if (!i::FLAG_use_ic) return;
432 if (!i::FLAG_opt) return;
433 if (i::FLAG_always_opt) return;
434 if (i::FLAG_jitless) return;
435 if (i::FLAG_turboprop) return;
436 FLAG_allow_natives_syntax = true;
437
438 CcTest::InitializeVM();
439 LocalContext context;
440 v8::HandleScope scope(context->GetIsolate());
441 Isolate* isolate = CcTest::i_isolate();
442
443 CompileRun(
444 "function min() { return Math.min.apply(null, arguments); }"
445 "function f(x) { return min(x, 0); }"
446 "%PrepareFunctionForOptimization(min);"
447 "%PrepareFunctionForOptimization(f);"
448 "f(1);");
449 Handle<JSFunction> min = GetFunction("min");
450 Handle<FeedbackVector> feedback_vector =
451 Handle<FeedbackVector>(min->feedback_vector(), isolate);
452 FeedbackSlot slot(6);
453 FeedbackNexus nexus(feedback_vector, slot);
454
455 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
456 CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
457 CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent());
458 CompileRun("%OptimizeFunctionOnNextCall(f); f(1);");
459 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
460 CHECK_EQ(SpeculationMode::kAllowSpeculation, nexus.GetSpeculationMode());
461 CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent());
462 CompileRun("f({});"); // Deoptimizes.
463 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
464 CHECK_EQ(SpeculationMode::kDisallowSpeculation, nexus.GetSpeculationMode());
465 CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent());
466 }
467
TEST(VectorLoadICStates)468 TEST(VectorLoadICStates) {
469 if (!i::FLAG_use_ic) return;
470 if (i::FLAG_always_opt) return;
471 FLAG_allow_natives_syntax = true;
472
473 CcTest::InitializeVM();
474 LocalContext context;
475 v8::HandleScope scope(context->GetIsolate());
476 Isolate* isolate = CcTest::i_isolate();
477
478 // Make sure function f has a call that uses a type feedback slot.
479 CompileRun(
480 "var o = { foo: 3 };"
481 "%EnsureFeedbackVectorForFunction(f);"
482 "function f(a) { return a.foo; } f(o);");
483 Handle<JSFunction> f = GetFunction("f");
484 // There should be one IC.
485 Handle<FeedbackVector> feedback_vector =
486 Handle<FeedbackVector>(f->feedback_vector(), isolate);
487 FeedbackSlot slot(0);
488 FeedbackNexus nexus(feedback_vector, slot);
489
490 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
491 // Verify that the monomorphic map is the one we expect.
492 v8::MaybeLocal<v8::Value> v8_o =
493 CcTest::global()->Get(context.local(), v8_str("o"));
494 Handle<JSObject> o =
495 Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
496 CHECK_EQ(o->map(), nexus.GetFirstMap());
497
498 // Now go polymorphic.
499 CompileRun("f({ blarg: 3, foo: 2 })");
500 CHECK_EQ(POLYMORPHIC, nexus.ic_state());
501
502 CompileRun(
503 "delete o.foo;"
504 "f(o)");
505 CHECK_EQ(POLYMORPHIC, nexus.ic_state());
506
507 CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
508 CHECK_EQ(POLYMORPHIC, nexus.ic_state());
509 MapHandles maps;
510 nexus.ExtractMaps(&maps);
511 CHECK_EQ(4, maps.size());
512
513 // Finally driven megamorphic.
514 CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
515 CHECK_EQ(MEGAMORPHIC, nexus.ic_state());
516 CHECK(nexus.GetFirstMap().is_null());
517
518 // After a collection, state should not be reset to PREMONOMORPHIC.
519 CcTest::CollectAllGarbage();
520 CHECK_EQ(MEGAMORPHIC, nexus.ic_state());
521 }
522
TEST(VectorLoadGlobalICSlotSharing)523 TEST(VectorLoadGlobalICSlotSharing) {
524 if (!i::FLAG_use_ic) return;
525 if (i::FLAG_always_opt) return;
526 FLAG_allow_natives_syntax = true;
527
528 CcTest::InitializeVM();
529 LocalContext context;
530 v8::HandleScope scope(context->GetIsolate());
531 Isolate* isolate = CcTest::i_isolate();
532
533 // Function f has 5 LoadGlobalICs: 3 for {o} references outside of "typeof"
534 // operator and 2 for {o} references inside "typeof" operator.
535 CompileRun(
536 "o = 10;"
537 "function f() {"
538 " var x = o || 10;"
539 " var y = typeof o;"
540 " return o , typeof o, x , y, o;"
541 "}"
542 "%EnsureFeedbackVectorForFunction(f);"
543 "f();");
544 Handle<JSFunction> f = GetFunction("f");
545 // There should be two IC slots for {o} references outside and inside
546 // typeof operator respectively.
547 Handle<FeedbackVector> feedback_vector =
548 Handle<FeedbackVector>(f->feedback_vector(), isolate);
549 FeedbackVectorHelper helper(feedback_vector);
550 CHECK_EQ(2, helper.slot_count());
551 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
552 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalInsideTypeof);
553 FeedbackSlot slot1 = helper.slot(0);
554 FeedbackSlot slot2 = helper.slot(1);
555 CHECK_EQ(MONOMORPHIC, FeedbackNexus(feedback_vector, slot1).ic_state());
556 CHECK_EQ(MONOMORPHIC, FeedbackNexus(feedback_vector, slot2).ic_state());
557 }
558
559
TEST(VectorLoadICOnSmi)560 TEST(VectorLoadICOnSmi) {
561 if (!i::FLAG_use_ic) return;
562 if (i::FLAG_always_opt) return;
563 FLAG_allow_natives_syntax = true;
564
565 CcTest::InitializeVM();
566 LocalContext context;
567 v8::HandleScope scope(context->GetIsolate());
568 Isolate* isolate = CcTest::i_isolate();
569 Heap* heap = isolate->heap();
570
571 // Make sure function f has a call that uses a type feedback slot.
572 CompileRun(
573 "var o = { foo: 3 };"
574 "%EnsureFeedbackVectorForFunction(f);"
575 "function f(a) { return a.foo; } f(34);");
576 Handle<JSFunction> f = GetFunction("f");
577 // There should be one IC.
578 Handle<FeedbackVector> feedback_vector =
579 Handle<FeedbackVector>(f->feedback_vector(), isolate);
580 FeedbackSlot slot(0);
581 FeedbackNexus nexus(feedback_vector, slot);
582 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
583 // Verify that the monomorphic map is the one we expect.
584 Map number_map = ReadOnlyRoots(heap).heap_number_map();
585 CHECK_EQ(number_map, nexus.GetFirstMap());
586
587 // Now go polymorphic on o.
588 CompileRun("f(o)");
589 CHECK_EQ(POLYMORPHIC, nexus.ic_state());
590
591 MapHandles maps;
592 nexus.ExtractMaps(&maps);
593 CHECK_EQ(2, maps.size());
594
595 // One of the maps should be the o map.
596 v8::MaybeLocal<v8::Value> v8_o =
597 CcTest::global()->Get(context.local(), v8_str("o"));
598 Handle<JSObject> o =
599 Handle<JSObject>::cast(v8::Utils::OpenHandle(*v8_o.ToLocalChecked()));
600 bool number_map_found = false;
601 bool o_map_found = false;
602 for (Handle<Map> current : maps) {
603 if (*current == number_map)
604 number_map_found = true;
605 else if (*current == o->map())
606 o_map_found = true;
607 }
608 CHECK(number_map_found && o_map_found);
609
610 // The degree of polymorphism doesn't change.
611 CompileRun("f(100)");
612 CHECK_EQ(POLYMORPHIC, nexus.ic_state());
613 MapHandles maps2;
614 nexus.ExtractMaps(&maps2);
615 CHECK_EQ(2, maps2.size());
616 }
617
618
TEST(ReferenceContextAllocatesNoSlots)619 TEST(ReferenceContextAllocatesNoSlots) {
620 if (!i::FLAG_use_ic) return;
621 if (i::FLAG_always_opt) return;
622 FLAG_allow_natives_syntax = true;
623
624 CcTest::InitializeVM();
625 LocalContext context;
626 v8::HandleScope scope(context->GetIsolate());
627 Isolate* isolate = CcTest::i_isolate();
628
629 {
630 CompileRun(
631 "function testvar(x) {"
632 " y = x;"
633 " y = a;"
634 " return y;"
635 "}"
636 "%EnsureFeedbackVectorForFunction(testvar);"
637 "a = 3;"
638 "testvar({});");
639
640 Handle<JSFunction> f = GetFunction("testvar");
641
642 // There should be two LOAD_ICs, one for a and one for y at the end.
643 Handle<FeedbackVector> feedback_vector =
644 handle(f->feedback_vector(), isolate);
645 FeedbackVectorHelper helper(feedback_vector);
646 CHECK_EQ(3, helper.slot_count());
647 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kStoreGlobalSloppy);
648 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
649 CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
650 }
651
652 {
653 CompileRun(
654 "function testprop(x) {"
655 " 'use strict';"
656 " x.blue = a;"
657 "}"
658 "%EnsureFeedbackVectorForFunction(testprop);"
659 "testprop({ blue: 3 });");
660
661 Handle<JSFunction> f = GetFunction("testprop");
662
663 // There should be one LOAD_IC, for the load of a.
664 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
665 FeedbackVectorHelper helper(feedback_vector);
666 CHECK_EQ(2, helper.slot_count());
667 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
668 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreNamedStrict);
669 }
670
671 {
672 CompileRun(
673 "function testpropfunc(x) {"
674 " x().blue = a;"
675 " return x().blue;"
676 "}"
677 "%EnsureFeedbackVectorForFunction(testpropfunc);"
678 "function makeresult() { return { blue: 3 }; }"
679 "testpropfunc(makeresult);");
680
681 Handle<JSFunction> f = GetFunction("testpropfunc");
682
683 // There should be 1 LOAD_GLOBAL_IC to load x (in both cases), 2 CALL_ICs
684 // to call x and a LOAD_IC to load blue.
685 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
686 FeedbackVectorHelper helper(feedback_vector);
687 CHECK_EQ(5, helper.slot_count());
688 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kCall);
689 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
690 CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kStoreNamedSloppy);
691 CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kCall);
692 CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kLoadProperty);
693 }
694
695 {
696 CompileRun(
697 "function testkeyedprop(x) {"
698 " x[0] = a;"
699 " return x[0];"
700 "}"
701 "%EnsureFeedbackVectorForFunction(testkeyedprop);"
702 "testkeyedprop([0, 1, 2]);");
703
704 Handle<JSFunction> f = GetFunction("testkeyedprop");
705
706 // There should be 1 LOAD_GLOBAL_ICs for the load of a, and one
707 // KEYED_LOAD_IC for the load of x[0] in the return statement.
708 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
709 FeedbackVectorHelper helper(feedback_vector);
710 CHECK_EQ(3, helper.slot_count());
711 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
712 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreKeyedSloppy);
713 CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed);
714 }
715
716 {
717 CompileRun(
718 "function testkeyedprop(x) {"
719 " 'use strict';"
720 " x[0] = a;"
721 " return x[0];"
722 "}"
723 "%EnsureFeedbackVectorForFunction(testkeyedprop);"
724 "testkeyedprop([0, 1, 2]);");
725
726 Handle<JSFunction> f = GetFunction("testkeyedprop");
727
728 // There should be 1 LOAD_GLOBAL_ICs for the load of a, and one
729 // KEYED_LOAD_IC for the load of x[0] in the return statement.
730 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
731 FeedbackVectorHelper helper(feedback_vector);
732 CHECK_EQ(3, helper.slot_count());
733 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
734 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreKeyedStrict);
735 CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kLoadKeyed);
736 }
737
738 {
739 CompileRun(
740 "function testcompound(x) {"
741 " 'use strict';"
742 " x.old = x.young = x.in_between = a;"
743 " return x.old + x.young;"
744 "}"
745 "%EnsureFeedbackVectorForFunction(testcompound);"
746 "testcompound({ old: 3, young: 3, in_between: 3 });");
747
748 Handle<JSFunction> f = GetFunction("testcompound");
749
750 // There should be 1 LOAD_GLOBAL_IC for load of a and 2 LOAD_ICs, for load
751 // of x.old and x.young.
752 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), isolate);
753 FeedbackVectorHelper helper(feedback_vector);
754 CHECK_EQ(7, helper.slot_count());
755 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
756 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreNamedStrict);
757 CHECK_SLOT_KIND(helper, 2, FeedbackSlotKind::kStoreNamedStrict);
758 CHECK_SLOT_KIND(helper, 3, FeedbackSlotKind::kStoreNamedStrict);
759 CHECK_SLOT_KIND(helper, 4, FeedbackSlotKind::kBinaryOp);
760 CHECK_SLOT_KIND(helper, 5, FeedbackSlotKind::kLoadProperty);
761 CHECK_SLOT_KIND(helper, 6, FeedbackSlotKind::kLoadProperty);
762 }
763 }
764
765
TEST(VectorStoreICBasic)766 TEST(VectorStoreICBasic) {
767 if (!i::FLAG_use_ic) return;
768 if (i::FLAG_always_opt) return;
769 FLAG_allow_natives_syntax = true;
770
771 CcTest::InitializeVM();
772 LocalContext context;
773 v8::HandleScope scope(context->GetIsolate());
774
775 CompileRun(
776 "function f(a) {"
777 " a.foo = 5;"
778 "};"
779 "%EnsureFeedbackVectorForFunction(f);"
780 "var a = { foo: 3 };"
781 "f(a);"
782 "f(a);"
783 "f(a);");
784 Handle<JSFunction> f = GetFunction("f");
785 // There should be one IC slot.
786 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), f->GetIsolate());
787 FeedbackVectorHelper helper(feedback_vector);
788 CHECK_EQ(1, helper.slot_count());
789 FeedbackSlot slot(0);
790 FeedbackNexus nexus(feedback_vector, slot);
791 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
792 }
793
TEST(StoreOwnIC)794 TEST(StoreOwnIC) {
795 if (!i::FLAG_use_ic) return;
796 if (i::FLAG_always_opt) return;
797 FLAG_allow_natives_syntax = true;
798
799 CcTest::InitializeVM();
800 LocalContext context;
801 v8::HandleScope scope(context->GetIsolate());
802
803 CompileRun(
804 "function f(v) {"
805 " return {a: 0, b: v, c: 0};"
806 "}"
807 "%EnsureFeedbackVectorForFunction(f);"
808 "f(1);"
809 "f(2);"
810 "f(3);");
811 Handle<JSFunction> f = GetFunction("f");
812 // There should be one IC slot.
813 Handle<FeedbackVector> feedback_vector(f->feedback_vector(), f->GetIsolate());
814 FeedbackVectorHelper helper(feedback_vector);
815 CHECK_EQ(2, helper.slot_count());
816 CHECK_SLOT_KIND(helper, 0, FeedbackSlotKind::kLiteral);
817 CHECK_SLOT_KIND(helper, 1, FeedbackSlotKind::kStoreOwnNamed);
818 FeedbackNexus nexus(feedback_vector, helper.slot(1));
819 CHECK_EQ(MONOMORPHIC, nexus.ic_state());
820 }
821
822 } // namespace
823
824 } // namespace internal
825 } // namespace v8
826