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 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 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. 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 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 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 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 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 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 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 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