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 17using namespace v8::internal; 18 19namespace { 20 21#define CHECK_SLOT_KIND(helper, index, expected_kind) \ 22 CHECK_EQ(expected_kind, helper.vector()->GetKind(helper.slot(index))); 23 24 25static 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 34TEST(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. 102TEST(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 156TEST(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 196TEST(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 236TEST(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 272TEST(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 327TEST(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 355TEST(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 414TEST(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 529TEST(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