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 <stdlib.h> 6 #include <utility> 7 8 #include "src/v8.h" 9 10 #include "src/compilation-cache.h" 11 #include "src/execution.h" 12 #include "src/factory.h" 13 #include "src/field-type.h" 14 #include "src/global-handles.h" 15 #include "src/ic/ic.h" 16 #include "src/macro-assembler.h" 17 #include "test/cctest/cctest.h" 18 #include "test/cctest/heap/heap-utils.h" 19 20 using namespace v8::base; 21 using namespace v8::internal; 22 23 #if V8_DOUBLE_FIELDS_UNBOXING 24 25 26 // 27 // Helper functions. 28 // 29 30 31 static void InitializeVerifiedMapDescriptors( 32 Map* map, DescriptorArray* descriptors, 33 LayoutDescriptor* layout_descriptor) { 34 map->InitializeDescriptors(descriptors, layout_descriptor); 35 CHECK(layout_descriptor->IsConsistentWithMap(map, true)); 36 } 37 38 39 static Handle<String> MakeString(const char* str) { 40 Isolate* isolate = CcTest::i_isolate(); 41 Factory* factory = isolate->factory(); 42 return factory->InternalizeUtf8String(str); 43 } 44 45 46 static Handle<String> MakeName(const char* str, int suffix) { 47 EmbeddedVector<char, 128> buffer; 48 SNPrintF(buffer, "%s%d", str, suffix); 49 return MakeString(buffer.start()); 50 } 51 52 53 Handle<JSObject> GetObject(const char* name) { 54 return Handle<JSObject>::cast( 55 v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast( 56 CcTest::global() 57 ->Get(v8::Isolate::GetCurrent()->GetCurrentContext(), 58 v8_str(name)) 59 .ToLocalChecked()))); 60 } 61 62 63 static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { 64 if (obj->IsUnboxedDoubleField(field_index)) { 65 return obj->RawFastDoublePropertyAt(field_index); 66 } else { 67 Object* value = obj->RawFastPropertyAt(field_index); 68 CHECK(value->IsMutableHeapNumber()); 69 return HeapNumber::cast(value)->value(); 70 } 71 } 72 73 const int kNumberOfBits = 32; 74 75 76 enum TestPropertyKind { 77 PROP_CONSTANT, 78 PROP_SMI, 79 PROP_DOUBLE, 80 PROP_TAGGED, 81 PROP_KIND_NUMBER 82 }; 83 84 static Representation representations[PROP_KIND_NUMBER] = { 85 Representation::None(), Representation::Smi(), Representation::Double(), 86 Representation::Tagged()}; 87 88 89 static Handle<DescriptorArray> CreateDescriptorArray(Isolate* isolate, 90 TestPropertyKind* props, 91 int kPropsCount) { 92 Factory* factory = isolate->factory(); 93 94 Handle<String> func_name = factory->InternalizeUtf8String("func"); 95 Handle<JSFunction> func = factory->NewFunction(func_name); 96 97 Handle<DescriptorArray> descriptors = 98 DescriptorArray::Allocate(isolate, 0, kPropsCount); 99 100 int next_field_offset = 0; 101 for (int i = 0; i < kPropsCount; i++) { 102 EmbeddedVector<char, 64> buffer; 103 SNPrintF(buffer, "prop%d", i); 104 Handle<String> name = factory->InternalizeUtf8String(buffer.start()); 105 106 TestPropertyKind kind = props[i]; 107 108 if (kind == PROP_CONSTANT) { 109 DataConstantDescriptor d(name, func, NONE); 110 descriptors->Append(&d); 111 112 } else { 113 DataDescriptor f(name, next_field_offset, NONE, representations[kind]); 114 next_field_offset += f.GetDetails().field_width_in_words(); 115 descriptors->Append(&f); 116 } 117 } 118 return descriptors; 119 } 120 121 122 TEST(LayoutDescriptorBasicFast) { 123 CcTest::InitializeVM(); 124 v8::HandleScope scope(CcTest::isolate()); 125 126 LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout(); 127 128 CHECK(!layout_desc->IsSlowLayout()); 129 CHECK(layout_desc->IsFastPointerLayout()); 130 CHECK_EQ(kSmiValueSize, layout_desc->capacity()); 131 132 for (int i = 0; i < kSmiValueSize + 13; i++) { 133 CHECK_EQ(true, layout_desc->IsTagged(i)); 134 } 135 CHECK_EQ(true, layout_desc->IsTagged(-1)); 136 CHECK_EQ(true, layout_desc->IsTagged(-12347)); 137 CHECK_EQ(true, layout_desc->IsTagged(15635)); 138 CHECK(layout_desc->IsFastPointerLayout()); 139 140 for (int i = 0; i < kSmiValueSize; i++) { 141 layout_desc = layout_desc->SetTaggedForTesting(i, false); 142 CHECK_EQ(false, layout_desc->IsTagged(i)); 143 layout_desc = layout_desc->SetTaggedForTesting(i, true); 144 CHECK_EQ(true, layout_desc->IsTagged(i)); 145 } 146 CHECK(layout_desc->IsFastPointerLayout()); 147 148 int sequence_length; 149 CHECK_EQ(true, layout_desc->IsTagged(0, std::numeric_limits<int>::max(), 150 &sequence_length)); 151 CHECK_EQ(std::numeric_limits<int>::max(), sequence_length); 152 153 CHECK_EQ(true, layout_desc->IsTagged(0, 7, &sequence_length)); 154 CHECK_EQ(7, sequence_length); 155 } 156 157 158 TEST(LayoutDescriptorBasicSlow) { 159 CcTest::InitializeVM(); 160 Isolate* isolate = CcTest::i_isolate(); 161 v8::HandleScope scope(CcTest::isolate()); 162 163 Handle<LayoutDescriptor> layout_descriptor; 164 const int kPropsCount = kSmiValueSize * 3; 165 TestPropertyKind props[kPropsCount]; 166 for (int i = 0; i < kPropsCount; i++) { 167 // All properties tagged. 168 props[i] = PROP_TAGGED; 169 } 170 171 { 172 Handle<DescriptorArray> descriptors = 173 CreateDescriptorArray(isolate, props, kPropsCount); 174 175 Handle<Map> map = Map::Create(isolate, kPropsCount); 176 177 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 178 CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 179 CHECK_EQ(kSmiValueSize, layout_descriptor->capacity()); 180 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 181 } 182 183 props[0] = PROP_DOUBLE; 184 props[kPropsCount - 1] = PROP_DOUBLE; 185 186 Handle<DescriptorArray> descriptors = 187 CreateDescriptorArray(isolate, props, kPropsCount); 188 189 { 190 int inobject_properties = kPropsCount - 1; 191 Handle<Map> map = Map::Create(isolate, inobject_properties); 192 193 // Should be fast as the only double property is the first one. 194 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 195 CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 196 CHECK(!layout_descriptor->IsSlowLayout()); 197 CHECK(!layout_descriptor->IsFastPointerLayout()); 198 199 CHECK_EQ(false, layout_descriptor->IsTagged(0)); 200 for (int i = 1; i < kPropsCount; i++) { 201 CHECK_EQ(true, layout_descriptor->IsTagged(i)); 202 } 203 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 204 } 205 206 { 207 int inobject_properties = kPropsCount; 208 Handle<Map> map = Map::Create(isolate, inobject_properties); 209 210 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 211 CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 212 CHECK(layout_descriptor->IsSlowLayout()); 213 CHECK(!layout_descriptor->IsFastPointerLayout()); 214 CHECK(layout_descriptor->capacity() > kSmiValueSize); 215 216 CHECK_EQ(false, layout_descriptor->IsTagged(0)); 217 CHECK_EQ(false, layout_descriptor->IsTagged(kPropsCount - 1)); 218 for (int i = 1; i < kPropsCount - 1; i++) { 219 CHECK_EQ(true, layout_descriptor->IsTagged(i)); 220 } 221 222 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 223 224 // Here we have truly slow layout descriptor, so play with the bits. 225 CHECK_EQ(true, layout_descriptor->IsTagged(-1)); 226 CHECK_EQ(true, layout_descriptor->IsTagged(-12347)); 227 CHECK_EQ(true, layout_descriptor->IsTagged(15635)); 228 229 LayoutDescriptor* layout_desc = *layout_descriptor; 230 // Play with the bits but leave it in consistent state with map at the end. 231 for (int i = 1; i < kPropsCount - 1; i++) { 232 layout_desc = layout_desc->SetTaggedForTesting(i, false); 233 CHECK_EQ(false, layout_desc->IsTagged(i)); 234 layout_desc = layout_desc->SetTaggedForTesting(i, true); 235 CHECK_EQ(true, layout_desc->IsTagged(i)); 236 } 237 CHECK(layout_desc->IsSlowLayout()); 238 CHECK(!layout_desc->IsFastPointerLayout()); 239 CHECK(layout_descriptor->IsConsistentWithMap(*map, true)); 240 } 241 } 242 243 244 static void TestLayoutDescriptorQueries(int layout_descriptor_length, 245 int* bit_flip_positions, 246 int max_sequence_length) { 247 Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::NewForTesting( 248 CcTest::i_isolate(), layout_descriptor_length); 249 layout_descriptor_length = layout_descriptor->capacity(); 250 LayoutDescriptor* layout_desc = *layout_descriptor; 251 252 { 253 // Fill in the layout descriptor. 254 int cur_bit_flip_index = 0; 255 bool tagged = true; 256 for (int i = 0; i < layout_descriptor_length; i++) { 257 if (i == bit_flip_positions[cur_bit_flip_index]) { 258 tagged = !tagged; 259 ++cur_bit_flip_index; 260 CHECK(i < bit_flip_positions[cur_bit_flip_index]); // check test data 261 } 262 layout_desc = layout_desc->SetTaggedForTesting(i, tagged); 263 } 264 } 265 266 if (layout_desc->IsFastPointerLayout()) { 267 return; 268 } 269 270 { 271 // Check queries. 272 int cur_bit_flip_index = 0; 273 bool tagged = true; 274 for (int i = 0; i < layout_descriptor_length; i++) { 275 if (i == bit_flip_positions[cur_bit_flip_index]) { 276 tagged = !tagged; 277 ++cur_bit_flip_index; 278 } 279 CHECK_EQ(tagged, layout_desc->IsTagged(i)); 280 281 int next_bit_flip_position = bit_flip_positions[cur_bit_flip_index]; 282 int expected_sequence_length; 283 if (next_bit_flip_position < layout_desc->capacity()) { 284 expected_sequence_length = next_bit_flip_position - i; 285 } else { 286 expected_sequence_length = tagged ? std::numeric_limits<int>::max() 287 : (layout_desc->capacity() - i); 288 } 289 expected_sequence_length = 290 Min(expected_sequence_length, max_sequence_length); 291 int sequence_length; 292 CHECK_EQ(tagged, 293 layout_desc->IsTagged(i, max_sequence_length, &sequence_length)); 294 CHECK(sequence_length > 0); 295 296 CHECK_EQ(expected_sequence_length, sequence_length); 297 } 298 299 int sequence_length; 300 CHECK_EQ(true, 301 layout_desc->IsTagged(layout_descriptor_length, 302 max_sequence_length, &sequence_length)); 303 CHECK_EQ(max_sequence_length, sequence_length); 304 } 305 } 306 307 308 static void TestLayoutDescriptorQueriesFast(int max_sequence_length) { 309 { 310 LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout(); 311 int sequence_length; 312 for (int i = 0; i < kNumberOfBits; i++) { 313 CHECK_EQ(true, 314 layout_desc->IsTagged(i, max_sequence_length, &sequence_length)); 315 CHECK(sequence_length > 0); 316 CHECK_EQ(max_sequence_length, sequence_length); 317 } 318 } 319 320 { 321 int bit_flip_positions[] = {1000}; 322 TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions, 323 max_sequence_length); 324 } 325 326 { 327 int bit_flip_positions[] = {0, 1000}; 328 TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions, 329 max_sequence_length); 330 } 331 332 { 333 int bit_flip_positions[kNumberOfBits + 1]; 334 for (int i = 0; i <= kNumberOfBits; i++) { 335 bit_flip_positions[i] = i; 336 } 337 TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions, 338 max_sequence_length); 339 } 340 341 { 342 int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30, 1000}; 343 TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions, 344 max_sequence_length); 345 } 346 347 { 348 int bit_flip_positions[] = {0, 1, 2, 3, 5, 7, 9, 349 12, 15, 18, 22, 26, 29, 1000}; 350 TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions, 351 max_sequence_length); 352 } 353 } 354 355 356 TEST(LayoutDescriptorQueriesFastLimited7) { 357 CcTest::InitializeVM(); 358 v8::HandleScope scope(CcTest::isolate()); 359 360 TestLayoutDescriptorQueriesFast(7); 361 } 362 363 364 TEST(LayoutDescriptorQueriesFastLimited13) { 365 CcTest::InitializeVM(); 366 v8::HandleScope scope(CcTest::isolate()); 367 368 TestLayoutDescriptorQueriesFast(13); 369 } 370 371 372 TEST(LayoutDescriptorQueriesFastUnlimited) { 373 CcTest::InitializeVM(); 374 v8::HandleScope scope(CcTest::isolate()); 375 376 TestLayoutDescriptorQueriesFast(std::numeric_limits<int>::max()); 377 } 378 379 380 static void TestLayoutDescriptorQueriesSlow(int max_sequence_length) { 381 { 382 int bit_flip_positions[] = {10000}; 383 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 384 max_sequence_length); 385 } 386 387 { 388 int bit_flip_positions[] = {0, 10000}; 389 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 390 max_sequence_length); 391 } 392 393 { 394 int bit_flip_positions[kMaxNumberOfDescriptors + 1]; 395 for (int i = 0; i < kMaxNumberOfDescriptors; i++) { 396 bit_flip_positions[i] = i; 397 } 398 bit_flip_positions[kMaxNumberOfDescriptors] = 10000; 399 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 400 max_sequence_length); 401 } 402 403 { 404 int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30, 405 37, 54, 80, 99, 383, 10000}; 406 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 407 max_sequence_length); 408 } 409 410 { 411 int bit_flip_positions[] = {0, 10, 20, 30, 50, 70, 90, 412 120, 150, 180, 220, 260, 290, 10000}; 413 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 414 max_sequence_length); 415 } 416 417 { 418 int bit_flip_positions[kMaxNumberOfDescriptors + 1]; 419 int cur = 0; 420 for (int i = 0; i < kMaxNumberOfDescriptors; i++) { 421 bit_flip_positions[i] = cur; 422 cur = (cur + 1) * 2; 423 } 424 CHECK(cur < 10000); 425 bit_flip_positions[kMaxNumberOfDescriptors] = 10000; 426 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 427 max_sequence_length); 428 } 429 430 { 431 int bit_flip_positions[kMaxNumberOfDescriptors + 1]; 432 int cur = 3; 433 for (int i = 0; i < kMaxNumberOfDescriptors; i++) { 434 bit_flip_positions[i] = cur; 435 cur = (cur + 1) * 2; 436 } 437 CHECK(cur < 10000); 438 bit_flip_positions[kMaxNumberOfDescriptors] = 10000; 439 TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions, 440 max_sequence_length); 441 } 442 } 443 444 445 TEST(LayoutDescriptorQueriesSlowLimited7) { 446 CcTest::InitializeVM(); 447 v8::HandleScope scope(CcTest::isolate()); 448 449 TestLayoutDescriptorQueriesSlow(7); 450 } 451 452 453 TEST(LayoutDescriptorQueriesSlowLimited13) { 454 CcTest::InitializeVM(); 455 v8::HandleScope scope(CcTest::isolate()); 456 457 TestLayoutDescriptorQueriesSlow(13); 458 } 459 460 461 TEST(LayoutDescriptorQueriesSlowLimited42) { 462 CcTest::InitializeVM(); 463 v8::HandleScope scope(CcTest::isolate()); 464 465 TestLayoutDescriptorQueriesSlow(42); 466 } 467 468 469 TEST(LayoutDescriptorQueriesSlowUnlimited) { 470 CcTest::InitializeVM(); 471 v8::HandleScope scope(CcTest::isolate()); 472 473 TestLayoutDescriptorQueriesSlow(std::numeric_limits<int>::max()); 474 } 475 476 477 TEST(LayoutDescriptorCreateNewFast) { 478 CcTest::InitializeVM(); 479 Isolate* isolate = CcTest::i_isolate(); 480 v8::HandleScope scope(CcTest::isolate()); 481 482 Handle<LayoutDescriptor> layout_descriptor; 483 TestPropertyKind props[] = { 484 PROP_CONSTANT, 485 PROP_TAGGED, // field #0 486 PROP_CONSTANT, 487 PROP_DOUBLE, // field #1 488 PROP_CONSTANT, 489 PROP_TAGGED, // field #2 490 PROP_CONSTANT, 491 }; 492 const int kPropsCount = arraysize(props); 493 494 Handle<DescriptorArray> descriptors = 495 CreateDescriptorArray(isolate, props, kPropsCount); 496 497 { 498 Handle<Map> map = Map::Create(isolate, 0); 499 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 500 CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 501 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 502 } 503 504 { 505 Handle<Map> map = Map::Create(isolate, 1); 506 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 507 CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 508 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 509 } 510 511 { 512 Handle<Map> map = Map::Create(isolate, 2); 513 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 514 CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 515 CHECK(!layout_descriptor->IsSlowLayout()); 516 CHECK_EQ(true, layout_descriptor->IsTagged(0)); 517 CHECK_EQ(false, layout_descriptor->IsTagged(1)); 518 CHECK_EQ(true, layout_descriptor->IsTagged(2)); 519 CHECK_EQ(true, layout_descriptor->IsTagged(125)); 520 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 521 } 522 } 523 524 525 TEST(LayoutDescriptorCreateNewSlow) { 526 CcTest::InitializeVM(); 527 Isolate* isolate = CcTest::i_isolate(); 528 v8::HandleScope scope(CcTest::isolate()); 529 530 Handle<LayoutDescriptor> layout_descriptor; 531 const int kPropsCount = kSmiValueSize * 3; 532 TestPropertyKind props[kPropsCount]; 533 for (int i = 0; i < kPropsCount; i++) { 534 props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER); 535 } 536 537 Handle<DescriptorArray> descriptors = 538 CreateDescriptorArray(isolate, props, kPropsCount); 539 540 { 541 Handle<Map> map = Map::Create(isolate, 0); 542 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 543 CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 544 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 545 } 546 547 { 548 Handle<Map> map = Map::Create(isolate, 1); 549 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 550 CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 551 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 552 } 553 554 { 555 Handle<Map> map = Map::Create(isolate, 2); 556 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 557 CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 558 CHECK(!layout_descriptor->IsSlowLayout()); 559 CHECK_EQ(true, layout_descriptor->IsTagged(0)); 560 CHECK_EQ(false, layout_descriptor->IsTagged(1)); 561 CHECK_EQ(true, layout_descriptor->IsTagged(2)); 562 CHECK_EQ(true, layout_descriptor->IsTagged(125)); 563 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 564 } 565 566 { 567 int inobject_properties = kPropsCount / 2; 568 Handle<Map> map = Map::Create(isolate, inobject_properties); 569 layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); 570 CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); 571 CHECK(layout_descriptor->IsSlowLayout()); 572 for (int i = 0; i < inobject_properties; i++) { 573 // PROP_DOUBLE has index 1 among DATA properties. 574 const bool tagged = (i % (PROP_KIND_NUMBER - 1)) != 1; 575 CHECK_EQ(tagged, layout_descriptor->IsTagged(i)); 576 } 577 // Every property after inobject_properties must be tagged. 578 for (int i = inobject_properties; i < kPropsCount; i++) { 579 CHECK_EQ(true, layout_descriptor->IsTagged(i)); 580 } 581 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 582 583 // Now test LayoutDescriptor::cast_gc_safe(). 584 Handle<LayoutDescriptor> layout_descriptor_copy = 585 LayoutDescriptor::New(map, descriptors, kPropsCount); 586 587 LayoutDescriptor* layout_desc = *layout_descriptor; 588 CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc)); 589 CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc)); 590 CHECK(layout_descriptor->IsFixedTypedArrayBase()); 591 // Now make it look like a forwarding pointer to layout_descriptor_copy. 592 MapWord map_word = layout_desc->map_word(); 593 CHECK(!map_word.IsForwardingAddress()); 594 layout_desc->set_map_word( 595 MapWord::FromForwardingAddress(*layout_descriptor_copy)); 596 CHECK(layout_desc->map_word().IsForwardingAddress()); 597 CHECK_EQ(*layout_descriptor_copy, 598 LayoutDescriptor::cast_gc_safe(layout_desc)); 599 600 // Restore it back. 601 layout_desc->set_map_word(map_word); 602 CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc)); 603 } 604 } 605 606 607 static Handle<LayoutDescriptor> TestLayoutDescriptorAppend( 608 Isolate* isolate, int inobject_properties, TestPropertyKind* props, 609 int kPropsCount) { 610 Factory* factory = isolate->factory(); 611 612 Handle<String> func_name = factory->InternalizeUtf8String("func"); 613 Handle<JSFunction> func = factory->NewFunction(func_name); 614 615 Handle<DescriptorArray> descriptors = 616 DescriptorArray::Allocate(isolate, 0, kPropsCount); 617 618 Handle<Map> map = Map::Create(isolate, inobject_properties); 619 map->InitializeDescriptors(*descriptors, 620 LayoutDescriptor::FastPointerLayout()); 621 622 int next_field_offset = 0; 623 for (int i = 0; i < kPropsCount; i++) { 624 EmbeddedVector<char, 64> buffer; 625 SNPrintF(buffer, "prop%d", i); 626 Handle<String> name = factory->InternalizeUtf8String(buffer.start()); 627 628 Handle<LayoutDescriptor> layout_descriptor; 629 TestPropertyKind kind = props[i]; 630 if (kind == PROP_CONSTANT) { 631 DataConstantDescriptor d(name, func, NONE); 632 layout_descriptor = LayoutDescriptor::ShareAppend(map, d.GetDetails()); 633 descriptors->Append(&d); 634 635 } else { 636 DataDescriptor f(name, next_field_offset, NONE, representations[kind]); 637 int field_width_in_words = f.GetDetails().field_width_in_words(); 638 next_field_offset += field_width_in_words; 639 layout_descriptor = LayoutDescriptor::ShareAppend(map, f.GetDetails()); 640 descriptors->Append(&f); 641 642 int field_index = f.GetDetails().field_index(); 643 bool is_inobject = field_index < map->GetInObjectProperties(); 644 for (int bit = 0; bit < field_width_in_words; bit++) { 645 CHECK_EQ(is_inobject && (kind == PROP_DOUBLE), 646 !layout_descriptor->IsTagged(field_index + bit)); 647 } 648 CHECK(layout_descriptor->IsTagged(next_field_offset)); 649 } 650 map->InitializeDescriptors(*descriptors, *layout_descriptor); 651 } 652 Handle<LayoutDescriptor> layout_descriptor(map->layout_descriptor(), isolate); 653 CHECK(layout_descriptor->IsConsistentWithMap(*map, true)); 654 return layout_descriptor; 655 } 656 657 658 TEST(LayoutDescriptorAppend) { 659 CcTest::InitializeVM(); 660 Isolate* isolate = CcTest::i_isolate(); 661 v8::HandleScope scope(CcTest::isolate()); 662 663 Handle<LayoutDescriptor> layout_descriptor; 664 const int kPropsCount = kSmiValueSize * 3; 665 TestPropertyKind props[kPropsCount]; 666 for (int i = 0; i < kPropsCount; i++) { 667 props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER); 668 } 669 670 layout_descriptor = 671 TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount); 672 CHECK(!layout_descriptor->IsSlowLayout()); 673 674 layout_descriptor = 675 TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount); 676 CHECK(!layout_descriptor->IsSlowLayout()); 677 678 layout_descriptor = 679 TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount); 680 CHECK(!layout_descriptor->IsSlowLayout()); 681 682 layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2, 683 props, kPropsCount); 684 CHECK(layout_descriptor->IsSlowLayout()); 685 686 layout_descriptor = 687 TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount); 688 CHECK(layout_descriptor->IsSlowLayout()); 689 } 690 691 692 TEST(LayoutDescriptorAppendAllDoubles) { 693 CcTest::InitializeVM(); 694 Isolate* isolate = CcTest::i_isolate(); 695 v8::HandleScope scope(CcTest::isolate()); 696 697 Handle<LayoutDescriptor> layout_descriptor; 698 const int kPropsCount = kSmiValueSize * 3; 699 TestPropertyKind props[kPropsCount]; 700 for (int i = 0; i < kPropsCount; i++) { 701 props[i] = PROP_DOUBLE; 702 } 703 704 layout_descriptor = 705 TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount); 706 CHECK(!layout_descriptor->IsSlowLayout()); 707 708 layout_descriptor = 709 TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount); 710 CHECK(!layout_descriptor->IsSlowLayout()); 711 712 layout_descriptor = 713 TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount); 714 CHECK(!layout_descriptor->IsSlowLayout()); 715 716 layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize + 1, 717 props, kPropsCount); 718 CHECK(layout_descriptor->IsSlowLayout()); 719 720 layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2, 721 props, kPropsCount); 722 CHECK(layout_descriptor->IsSlowLayout()); 723 724 layout_descriptor = 725 TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount); 726 CHECK(layout_descriptor->IsSlowLayout()); 727 728 { 729 // Ensure layout descriptor switches into slow mode at the right moment. 730 layout_descriptor = 731 TestLayoutDescriptorAppend(isolate, kPropsCount, props, kSmiValueSize); 732 CHECK(!layout_descriptor->IsSlowLayout()); 733 734 layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props, 735 kSmiValueSize + 1); 736 CHECK(layout_descriptor->IsSlowLayout()); 737 } 738 } 739 740 741 static Handle<LayoutDescriptor> TestLayoutDescriptorAppendIfFastOrUseFull( 742 Isolate* isolate, int inobject_properties, 743 Handle<DescriptorArray> descriptors, int number_of_descriptors) { 744 Handle<Map> initial_map = Map::Create(isolate, inobject_properties); 745 746 Handle<LayoutDescriptor> full_layout_descriptor = LayoutDescriptor::New( 747 initial_map, descriptors, descriptors->number_of_descriptors()); 748 749 int nof = 0; 750 bool switched_to_slow_mode = false; 751 752 // This method calls LayoutDescriptor::AppendIfFastOrUseFull() internally 753 // and does all the required map-descriptors related book keeping. 754 Handle<Map> last_map = Map::AddMissingTransitionsForTesting( 755 initial_map, descriptors, full_layout_descriptor); 756 757 // Follow back pointers to construct a sequence of maps from |map| 758 // to |last_map|. 759 int descriptors_length = descriptors->number_of_descriptors(); 760 std::vector<Handle<Map>> maps(descriptors_length); 761 { 762 CHECK(last_map->is_stable()); 763 Map* map = *last_map; 764 for (int i = 0; i < descriptors_length; i++) { 765 maps[descriptors_length - 1 - i] = handle(map, isolate); 766 Object* maybe_map = map->GetBackPointer(); 767 CHECK(maybe_map->IsMap()); 768 map = Map::cast(maybe_map); 769 CHECK(!map->is_stable()); 770 } 771 CHECK_EQ(1, maps[0]->NumberOfOwnDescriptors()); 772 } 773 774 Handle<Map> map; 775 // Now check layout descriptors of all intermediate maps. 776 for (int i = 0; i < number_of_descriptors; i++) { 777 PropertyDetails details = descriptors->GetDetails(i); 778 map = maps[i]; 779 LayoutDescriptor* layout_desc = map->layout_descriptor(); 780 781 if (layout_desc->IsSlowLayout()) { 782 switched_to_slow_mode = true; 783 CHECK_EQ(*full_layout_descriptor, layout_desc); 784 } else { 785 CHECK(!switched_to_slow_mode); 786 if (details.type() == DATA) { 787 nof++; 788 int field_index = details.field_index(); 789 int field_width_in_words = details.field_width_in_words(); 790 791 bool is_inobject = field_index < map->GetInObjectProperties(); 792 for (int bit = 0; bit < field_width_in_words; bit++) { 793 CHECK_EQ(is_inobject && details.representation().IsDouble(), 794 !layout_desc->IsTagged(field_index + bit)); 795 } 796 CHECK(layout_desc->IsTagged(field_index + field_width_in_words)); 797 } 798 } 799 CHECK(map->layout_descriptor()->IsConsistentWithMap(*map)); 800 } 801 802 Handle<LayoutDescriptor> layout_descriptor(map->GetLayoutDescriptor(), 803 isolate); 804 CHECK(layout_descriptor->IsConsistentWithMap(*map)); 805 return layout_descriptor; 806 } 807 808 809 TEST(LayoutDescriptorAppendIfFastOrUseFull) { 810 CcTest::InitializeVM(); 811 Isolate* isolate = CcTest::i_isolate(); 812 v8::HandleScope scope(CcTest::isolate()); 813 814 Handle<LayoutDescriptor> layout_descriptor; 815 const int kPropsCount = kSmiValueSize * 3; 816 TestPropertyKind props[kPropsCount]; 817 for (int i = 0; i < kPropsCount; i++) { 818 props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER); 819 } 820 Handle<DescriptorArray> descriptors = 821 CreateDescriptorArray(isolate, props, kPropsCount); 822 823 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 824 isolate, 0, descriptors, kPropsCount); 825 CHECK(!layout_descriptor->IsSlowLayout()); 826 827 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 828 isolate, 13, descriptors, kPropsCount); 829 CHECK(!layout_descriptor->IsSlowLayout()); 830 831 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 832 isolate, kSmiValueSize, descriptors, kPropsCount); 833 CHECK(!layout_descriptor->IsSlowLayout()); 834 835 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 836 isolate, kSmiValueSize * 2, descriptors, kPropsCount); 837 CHECK(layout_descriptor->IsSlowLayout()); 838 839 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 840 isolate, kPropsCount, descriptors, kPropsCount); 841 CHECK(layout_descriptor->IsSlowLayout()); 842 } 843 844 845 TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles) { 846 CcTest::InitializeVM(); 847 Isolate* isolate = CcTest::i_isolate(); 848 v8::HandleScope scope(CcTest::isolate()); 849 850 Handle<LayoutDescriptor> layout_descriptor; 851 const int kPropsCount = kSmiValueSize * 3; 852 TestPropertyKind props[kPropsCount]; 853 for (int i = 0; i < kPropsCount; i++) { 854 props[i] = PROP_DOUBLE; 855 } 856 Handle<DescriptorArray> descriptors = 857 CreateDescriptorArray(isolate, props, kPropsCount); 858 859 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 860 isolate, 0, descriptors, kPropsCount); 861 CHECK(!layout_descriptor->IsSlowLayout()); 862 863 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 864 isolate, 13, descriptors, kPropsCount); 865 CHECK(!layout_descriptor->IsSlowLayout()); 866 867 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 868 isolate, kSmiValueSize, descriptors, kPropsCount); 869 CHECK(!layout_descriptor->IsSlowLayout()); 870 871 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 872 isolate, kSmiValueSize + 1, descriptors, kPropsCount); 873 CHECK(layout_descriptor->IsSlowLayout()); 874 875 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 876 isolate, kSmiValueSize * 2, descriptors, kPropsCount); 877 CHECK(layout_descriptor->IsSlowLayout()); 878 879 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 880 isolate, kPropsCount, descriptors, kPropsCount); 881 CHECK(layout_descriptor->IsSlowLayout()); 882 883 { 884 // Ensure layout descriptor switches into slow mode at the right moment. 885 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 886 isolate, kPropsCount, descriptors, kSmiValueSize); 887 CHECK(!layout_descriptor->IsSlowLayout()); 888 889 layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( 890 isolate, kPropsCount, descriptors, kSmiValueSize + 1); 891 CHECK(layout_descriptor->IsSlowLayout()); 892 } 893 } 894 895 896 TEST(Regress436816) { 897 CcTest::InitializeVM(); 898 Isolate* isolate = CcTest::i_isolate(); 899 Factory* factory = isolate->factory(); 900 v8::HandleScope scope(CcTest::isolate()); 901 902 const int kPropsCount = kSmiValueSize * 3; 903 TestPropertyKind props[kPropsCount]; 904 for (int i = 0; i < kPropsCount; i++) { 905 props[i] = PROP_DOUBLE; 906 } 907 Handle<DescriptorArray> descriptors = 908 CreateDescriptorArray(isolate, props, kPropsCount); 909 910 Handle<Map> map = Map::Create(isolate, kPropsCount); 911 Handle<LayoutDescriptor> layout_descriptor = 912 LayoutDescriptor::New(map, descriptors, kPropsCount); 913 map->InitializeDescriptors(*descriptors, *layout_descriptor); 914 915 Handle<JSObject> object = factory->NewJSObjectFromMap(map, TENURED); 916 917 Address fake_address = reinterpret_cast<Address>(~kHeapObjectTagMask); 918 HeapObject* fake_object = HeapObject::FromAddress(fake_address); 919 CHECK(fake_object->IsHeapObject()); 920 921 double boom_value = bit_cast<double>(fake_object); 922 for (int i = 0; i < kPropsCount; i++) { 923 FieldIndex index = FieldIndex::ForDescriptor(*map, i); 924 CHECK(map->IsUnboxedDoubleField(index)); 925 object->RawFastDoublePropertyAtPut(index, boom_value); 926 } 927 CHECK(object->HasFastProperties()); 928 CHECK(!object->map()->HasFastPointerLayout()); 929 930 Handle<Map> normalized_map = 931 Map::Normalize(map, KEEP_INOBJECT_PROPERTIES, "testing"); 932 JSObject::MigrateToMap(object, normalized_map); 933 CHECK(!object->HasFastProperties()); 934 CHECK(object->map()->HasFastPointerLayout()); 935 936 // Trigger GCs and heap verification. 937 CcTest::heap()->CollectAllGarbage(); 938 } 939 940 941 TEST(DescriptorArrayTrimming) { 942 CcTest::InitializeVM(); 943 v8::HandleScope scope(CcTest::isolate()); 944 Isolate* isolate = CcTest::i_isolate(); 945 946 const int kFieldCount = 128; 947 const int kSplitFieldIndex = 32; 948 const int kTrimmedLayoutDescriptorLength = 64; 949 950 Handle<FieldType> any_type = FieldType::Any(isolate); 951 Handle<Map> map = Map::Create(isolate, kFieldCount); 952 for (int i = 0; i < kSplitFieldIndex; i++) { 953 map = Map::CopyWithField(map, MakeName("prop", i), any_type, NONE, 954 Representation::Smi(), 955 INSERT_TRANSITION).ToHandleChecked(); 956 } 957 map = Map::CopyWithField(map, MakeName("dbl", kSplitFieldIndex), any_type, 958 NONE, Representation::Double(), 959 INSERT_TRANSITION).ToHandleChecked(); 960 CHECK(map->layout_descriptor()->IsConsistentWithMap(*map, true)); 961 CHECK(map->layout_descriptor()->IsSlowLayout()); 962 CHECK(map->owns_descriptors()); 963 CHECK_EQ(2, map->layout_descriptor()->length()); 964 965 { 966 // Add transitions to double fields. 967 v8::HandleScope scope(CcTest::isolate()); 968 969 Handle<Map> tmp_map = map; 970 for (int i = kSplitFieldIndex + 1; i < kFieldCount; i++) { 971 tmp_map = Map::CopyWithField(tmp_map, MakeName("dbl", i), any_type, NONE, 972 Representation::Double(), 973 INSERT_TRANSITION).ToHandleChecked(); 974 CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true)); 975 } 976 // Check that descriptors are shared. 977 CHECK(tmp_map->owns_descriptors()); 978 CHECK_EQ(map->instance_descriptors(), tmp_map->instance_descriptors()); 979 CHECK_EQ(map->layout_descriptor(), tmp_map->layout_descriptor()); 980 } 981 CHECK(map->layout_descriptor()->IsSlowLayout()); 982 CHECK_EQ(4, map->layout_descriptor()->length()); 983 984 // The unused tail of the layout descriptor is now "durty" because of sharing. 985 CHECK(map->layout_descriptor()->IsConsistentWithMap(*map)); 986 for (int i = kSplitFieldIndex + 1; i < kTrimmedLayoutDescriptorLength; i++) { 987 CHECK(!map->layout_descriptor()->IsTagged(i)); 988 } 989 CHECK_LT(map->NumberOfOwnDescriptors(), 990 map->instance_descriptors()->number_of_descriptors()); 991 992 // Call GC that should trim both |map|'s descriptor array and layout 993 // descriptor. 994 CcTest::heap()->CollectAllGarbage(); 995 996 // The unused tail of the layout descriptor is now "clean" again. 997 CHECK(map->layout_descriptor()->IsConsistentWithMap(*map, true)); 998 CHECK(map->owns_descriptors()); 999 CHECK_EQ(map->NumberOfOwnDescriptors(), 1000 map->instance_descriptors()->number_of_descriptors()); 1001 CHECK(map->layout_descriptor()->IsSlowLayout()); 1002 CHECK_EQ(2, map->layout_descriptor()->length()); 1003 1004 { 1005 // Add transitions to tagged fields. 1006 v8::HandleScope scope(CcTest::isolate()); 1007 1008 Handle<Map> tmp_map = map; 1009 for (int i = kSplitFieldIndex + 1; i < kFieldCount - 1; i++) { 1010 tmp_map = Map::CopyWithField(tmp_map, MakeName("tagged", i), any_type, 1011 NONE, Representation::Tagged(), 1012 INSERT_TRANSITION).ToHandleChecked(); 1013 CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true)); 1014 } 1015 tmp_map = Map::CopyWithField(tmp_map, MakeString("dbl"), any_type, NONE, 1016 Representation::Double(), 1017 INSERT_TRANSITION).ToHandleChecked(); 1018 CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true)); 1019 // Check that descriptors are shared. 1020 CHECK(tmp_map->owns_descriptors()); 1021 CHECK_EQ(map->instance_descriptors(), tmp_map->instance_descriptors()); 1022 } 1023 CHECK(map->layout_descriptor()->IsSlowLayout()); 1024 } 1025 1026 1027 TEST(DoScavenge) { 1028 CcTest::InitializeVM(); 1029 v8::HandleScope scope(CcTest::isolate()); 1030 Isolate* isolate = CcTest::i_isolate(); 1031 Factory* factory = isolate->factory(); 1032 1033 // The plan: create |obj| with double field in new space, do scanvenge so 1034 // that |obj| is moved to old space, construct a double value that looks like 1035 // a pointer to "from space" pointer. Do scavenge one more time and ensure 1036 // that it didn't crash or corrupt the double value stored in the object. 1037 1038 Handle<FieldType> any_type = FieldType::Any(isolate); 1039 Handle<Map> map = Map::Create(isolate, 10); 1040 map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, 1041 Representation::Double(), 1042 INSERT_TRANSITION).ToHandleChecked(); 1043 1044 // Create object in new space. 1045 Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED); 1046 1047 Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); 1048 obj->WriteToField(0, *heap_number); 1049 1050 { 1051 // Ensure the object is properly set up. 1052 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); 1053 CHECK(field_index.is_inobject() && field_index.is_double()); 1054 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); 1055 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); 1056 } 1057 CHECK(isolate->heap()->new_space()->Contains(*obj)); 1058 1059 // Do scavenge so that |obj| is moved to survivor space. 1060 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 1061 1062 // Create temp object in the new space. 1063 Handle<JSArray> temp = factory->NewJSArray(0, FAST_ELEMENTS); 1064 CHECK(isolate->heap()->new_space()->Contains(*temp)); 1065 1066 // Construct a double value that looks like a pointer to the new space object 1067 // and store it into the obj. 1068 Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize; 1069 double boom_value = bit_cast<double>(fake_object); 1070 1071 FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0); 1072 Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE); 1073 obj->FastPropertyAtPut(field_index, *boom_number); 1074 1075 // Now |obj| moves to old gen and it has a double field that looks like 1076 // a pointer to a from semi-space. 1077 CcTest::heap()->CollectGarbage(i::NEW_SPACE, "boom"); 1078 1079 CHECK(isolate->heap()->old_space()->Contains(*obj)); 1080 1081 CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index)); 1082 } 1083 1084 1085 TEST(DoScavengeWithIncrementalWriteBarrier) { 1086 if (FLAG_never_compact || !FLAG_incremental_marking) return; 1087 CcTest::InitializeVM(); 1088 v8::HandleScope scope(CcTest::isolate()); 1089 Isolate* isolate = CcTest::i_isolate(); 1090 Factory* factory = isolate->factory(); 1091 Heap* heap = CcTest::heap(); 1092 PagedSpace* old_space = heap->old_space(); 1093 1094 // The plan: create |obj_value| in old space and ensure that it is allocated 1095 // on evacuation candidate page, create |obj| with double and tagged fields 1096 // in new space and write |obj_value| to tagged field of |obj|, do two 1097 // scavenges to promote |obj| to old space, a GC in old space and ensure that 1098 // the tagged value was properly updated after candidates evacuation. 1099 1100 Handle<FieldType> any_type = FieldType::Any(isolate); 1101 Handle<Map> map = Map::Create(isolate, 10); 1102 map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, 1103 Representation::Double(), 1104 INSERT_TRANSITION).ToHandleChecked(); 1105 map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, 1106 Representation::Tagged(), 1107 INSERT_TRANSITION).ToHandleChecked(); 1108 1109 // Create |obj_value| in old space. 1110 Handle<HeapObject> obj_value; 1111 Page* ec_page; 1112 { 1113 AlwaysAllocateScope always_allocate(isolate); 1114 // Make sure |obj_value| is placed on an old-space evacuation candidate. 1115 heap::SimulateFullSpace(old_space); 1116 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); 1117 ec_page = Page::FromAddress(obj_value->address()); 1118 } 1119 1120 // Create object in new space. 1121 Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED); 1122 1123 Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); 1124 obj->WriteToField(0, *heap_number); 1125 obj->WriteToField(1, *obj_value); 1126 1127 { 1128 // Ensure the object is properly set up. 1129 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); 1130 CHECK(field_index.is_inobject() && field_index.is_double()); 1131 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); 1132 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); 1133 1134 field_index = FieldIndex::ForDescriptor(*map, 1); 1135 CHECK(field_index.is_inobject() && !field_index.is_double()); 1136 CHECK(!map->IsUnboxedDoubleField(field_index)); 1137 } 1138 CHECK(isolate->heap()->new_space()->Contains(*obj)); 1139 1140 // Heap is ready, force |ec_page| to become an evacuation candidate and 1141 // simulate incremental marking. 1142 FLAG_stress_compaction = true; 1143 FLAG_manual_evacuation_candidates_selection = true; 1144 ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); 1145 heap::SimulateIncrementalMarking(heap); 1146 // Disable stress compaction mode in order to let GC do scavenge. 1147 FLAG_stress_compaction = false; 1148 1149 // Check that everything is ready for triggering incremental write barrier 1150 // during scavenge (i.e. that |obj| is black and incremental marking is 1151 // in compacting mode and |obj_value|'s page is an evacuation candidate). 1152 IncrementalMarking* marking = heap->incremental_marking(); 1153 CHECK(marking->IsCompacting()); 1154 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); 1155 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); 1156 1157 // Trigger GCs so that |obj| moves to old gen. 1158 heap->CollectGarbage(i::NEW_SPACE); // in survivor space now 1159 heap->CollectGarbage(i::NEW_SPACE); // in old gen now 1160 1161 CHECK(isolate->heap()->old_space()->Contains(*obj)); 1162 CHECK(isolate->heap()->old_space()->Contains(*obj_value)); 1163 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); 1164 1165 heap->CollectGarbage(i::OLD_SPACE, "boom"); 1166 1167 // |obj_value| must be evacuated. 1168 CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); 1169 1170 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 1); 1171 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(field_index)); 1172 } 1173 1174 1175 static void TestLayoutDescriptorHelper(Isolate* isolate, 1176 int inobject_properties, 1177 Handle<DescriptorArray> descriptors, 1178 int number_of_descriptors) { 1179 Handle<Map> map = Map::Create(isolate, inobject_properties); 1180 1181 Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New( 1182 map, descriptors, descriptors->number_of_descriptors()); 1183 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); 1184 1185 LayoutDescriptorHelper helper(*map); 1186 bool all_fields_tagged = true; 1187 1188 int instance_size = map->instance_size(); 1189 1190 int end_offset = instance_size * 2; 1191 int first_non_tagged_field_offset = end_offset; 1192 for (int i = 0; i < number_of_descriptors; i++) { 1193 PropertyDetails details = descriptors->GetDetails(i); 1194 if (details.type() != DATA) continue; 1195 FieldIndex index = FieldIndex::ForDescriptor(*map, i); 1196 if (!index.is_inobject()) continue; 1197 all_fields_tagged &= !details.representation().IsDouble(); 1198 bool expected_tagged = !index.is_double(); 1199 if (!expected_tagged) { 1200 first_non_tagged_field_offset = 1201 Min(first_non_tagged_field_offset, index.offset()); 1202 } 1203 1204 int end_of_region_offset; 1205 CHECK_EQ(expected_tagged, helper.IsTagged(index.offset())); 1206 CHECK_EQ(expected_tagged, helper.IsTagged(index.offset(), instance_size, 1207 &end_of_region_offset)); 1208 CHECK(end_of_region_offset > 0); 1209 CHECK(end_of_region_offset % kPointerSize == 0); 1210 CHECK(end_of_region_offset <= instance_size); 1211 1212 for (int offset = index.offset(); offset < end_of_region_offset; 1213 offset += kPointerSize) { 1214 CHECK_EQ(expected_tagged, helper.IsTagged(index.offset())); 1215 } 1216 if (end_of_region_offset < instance_size) { 1217 CHECK_EQ(!expected_tagged, helper.IsTagged(end_of_region_offset)); 1218 } else { 1219 CHECK_EQ(true, helper.IsTagged(end_of_region_offset)); 1220 } 1221 } 1222 1223 for (int offset = 0; offset < JSObject::kHeaderSize; offset += kPointerSize) { 1224 // Header queries 1225 CHECK_EQ(true, helper.IsTagged(offset)); 1226 int end_of_region_offset; 1227 CHECK_EQ(true, helper.IsTagged(offset, end_offset, &end_of_region_offset)); 1228 CHECK_EQ(first_non_tagged_field_offset, end_of_region_offset); 1229 1230 // Out of bounds queries 1231 CHECK_EQ(true, helper.IsTagged(offset + instance_size)); 1232 } 1233 1234 CHECK_EQ(all_fields_tagged, helper.all_fields_tagged()); 1235 } 1236 1237 1238 TEST(LayoutDescriptorHelperMixed) { 1239 CcTest::InitializeVM(); 1240 Isolate* isolate = CcTest::i_isolate(); 1241 v8::HandleScope scope(CcTest::isolate()); 1242 1243 Handle<LayoutDescriptor> layout_descriptor; 1244 const int kPropsCount = kSmiValueSize * 3; 1245 TestPropertyKind props[kPropsCount]; 1246 for (int i = 0; i < kPropsCount; i++) { 1247 props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER); 1248 } 1249 Handle<DescriptorArray> descriptors = 1250 CreateDescriptorArray(isolate, props, kPropsCount); 1251 1252 TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount); 1253 1254 TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount); 1255 1256 TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount); 1257 1258 TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors, 1259 kPropsCount); 1260 1261 TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount); 1262 } 1263 1264 1265 TEST(LayoutDescriptorHelperAllTagged) { 1266 CcTest::InitializeVM(); 1267 Isolate* isolate = CcTest::i_isolate(); 1268 v8::HandleScope scope(CcTest::isolate()); 1269 1270 Handle<LayoutDescriptor> layout_descriptor; 1271 const int kPropsCount = kSmiValueSize * 3; 1272 TestPropertyKind props[kPropsCount]; 1273 for (int i = 0; i < kPropsCount; i++) { 1274 props[i] = PROP_TAGGED; 1275 } 1276 Handle<DescriptorArray> descriptors = 1277 CreateDescriptorArray(isolate, props, kPropsCount); 1278 1279 TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount); 1280 1281 TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount); 1282 1283 TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount); 1284 1285 TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors, 1286 kPropsCount); 1287 1288 TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount); 1289 } 1290 1291 1292 TEST(LayoutDescriptorHelperAllDoubles) { 1293 CcTest::InitializeVM(); 1294 Isolate* isolate = CcTest::i_isolate(); 1295 v8::HandleScope scope(CcTest::isolate()); 1296 1297 Handle<LayoutDescriptor> layout_descriptor; 1298 const int kPropsCount = kSmiValueSize * 3; 1299 TestPropertyKind props[kPropsCount]; 1300 for (int i = 0; i < kPropsCount; i++) { 1301 props[i] = PROP_DOUBLE; 1302 } 1303 Handle<DescriptorArray> descriptors = 1304 CreateDescriptorArray(isolate, props, kPropsCount); 1305 1306 TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount); 1307 1308 TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount); 1309 1310 TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount); 1311 1312 TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors, 1313 kPropsCount); 1314 1315 TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount); 1316 } 1317 1318 1319 TEST(LayoutDescriptorSharing) { 1320 CcTest::InitializeVM(); 1321 v8::HandleScope scope(CcTest::isolate()); 1322 Isolate* isolate = CcTest::i_isolate(); 1323 Handle<FieldType> any_type = FieldType::Any(isolate); 1324 1325 Handle<Map> split_map; 1326 { 1327 Handle<Map> map = Map::Create(isolate, 64); 1328 for (int i = 0; i < 32; i++) { 1329 Handle<String> name = MakeName("prop", i); 1330 map = Map::CopyWithField(map, name, any_type, NONE, Representation::Smi(), 1331 INSERT_TRANSITION).ToHandleChecked(); 1332 } 1333 split_map = Map::CopyWithField(map, MakeString("dbl"), any_type, NONE, 1334 Representation::Double(), 1335 INSERT_TRANSITION).ToHandleChecked(); 1336 } 1337 Handle<LayoutDescriptor> split_layout_descriptor( 1338 split_map->layout_descriptor(), isolate); 1339 CHECK(split_layout_descriptor->IsConsistentWithMap(*split_map, true)); 1340 CHECK(split_layout_descriptor->IsSlowLayout()); 1341 CHECK(split_map->owns_descriptors()); 1342 1343 Handle<Map> map1 = Map::CopyWithField(split_map, MakeString("foo"), any_type, 1344 NONE, Representation::Double(), 1345 INSERT_TRANSITION).ToHandleChecked(); 1346 CHECK(!split_map->owns_descriptors()); 1347 CHECK_EQ(*split_layout_descriptor, split_map->layout_descriptor()); 1348 1349 // Layout descriptors should be shared with |split_map|. 1350 CHECK(map1->owns_descriptors()); 1351 CHECK_EQ(*split_layout_descriptor, map1->layout_descriptor()); 1352 CHECK(map1->layout_descriptor()->IsConsistentWithMap(*map1, true)); 1353 1354 Handle<Map> map2 = Map::CopyWithField(split_map, MakeString("bar"), any_type, 1355 NONE, Representation::Tagged(), 1356 INSERT_TRANSITION).ToHandleChecked(); 1357 1358 // Layout descriptors should not be shared with |split_map|. 1359 CHECK(map2->owns_descriptors()); 1360 CHECK_NE(*split_layout_descriptor, map2->layout_descriptor()); 1361 CHECK(map2->layout_descriptor()->IsConsistentWithMap(*map2, true)); 1362 } 1363 1364 1365 static void TestWriteBarrier(Handle<Map> map, Handle<Map> new_map, 1366 int tagged_descriptor, int double_descriptor, 1367 bool check_tagged_value = true) { 1368 FLAG_stress_compaction = true; 1369 FLAG_manual_evacuation_candidates_selection = true; 1370 Isolate* isolate = CcTest::i_isolate(); 1371 Factory* factory = isolate->factory(); 1372 Heap* heap = CcTest::heap(); 1373 PagedSpace* old_space = heap->old_space(); 1374 1375 // The plan: create |obj| by |map| in old space, create |obj_value| in 1376 // new space and ensure that write barrier is triggered when |obj_value| is 1377 // written to property |tagged_descriptor| of |obj|. 1378 // Then migrate object to |new_map| and set proper value for property 1379 // |double_descriptor|. Call GC and ensure that it did not crash during 1380 // store buffer entries updating. 1381 1382 Handle<JSObject> obj; 1383 Handle<HeapObject> obj_value; 1384 { 1385 AlwaysAllocateScope always_allocate(isolate); 1386 obj = factory->NewJSObjectFromMap(map, TENURED); 1387 CHECK(old_space->Contains(*obj)); 1388 1389 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS); 1390 } 1391 1392 CHECK(heap->InNewSpace(*obj_value)); 1393 1394 { 1395 FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); 1396 const int n = 153; 1397 for (int i = 0; i < n; i++) { 1398 obj->FastPropertyAtPut(index, *obj_value); 1399 } 1400 } 1401 1402 // Migrate |obj| to |new_map| which should shift fields and put the 1403 // |boom_value| to the slot that was earlier recorded by write barrier. 1404 JSObject::MigrateToMap(obj, new_map); 1405 1406 Address fake_object = reinterpret_cast<Address>(*obj_value) + kPointerSize; 1407 double boom_value = bit_cast<double>(fake_object); 1408 1409 FieldIndex double_field_index = 1410 FieldIndex::ForDescriptor(*new_map, double_descriptor); 1411 CHECK(obj->IsUnboxedDoubleField(double_field_index)); 1412 obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); 1413 1414 // Trigger GC to evacuate all candidates. 1415 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); 1416 1417 if (check_tagged_value) { 1418 FieldIndex tagged_field_index = 1419 FieldIndex::ForDescriptor(*new_map, tagged_descriptor); 1420 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); 1421 } 1422 CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); 1423 } 1424 1425 1426 static void TestIncrementalWriteBarrier(Handle<Map> map, Handle<Map> new_map, 1427 int tagged_descriptor, 1428 int double_descriptor, 1429 bool check_tagged_value = true) { 1430 if (FLAG_never_compact || !FLAG_incremental_marking) return; 1431 FLAG_manual_evacuation_candidates_selection = true; 1432 Isolate* isolate = CcTest::i_isolate(); 1433 Factory* factory = isolate->factory(); 1434 Heap* heap = CcTest::heap(); 1435 PagedSpace* old_space = heap->old_space(); 1436 1437 // The plan: create |obj| by |map| in old space, create |obj_value| in 1438 // old space and ensure it end up in evacuation candidate page. Start 1439 // incremental marking and ensure that incremental write barrier is triggered 1440 // when |obj_value| is written to property |tagged_descriptor| of |obj|. 1441 // Then migrate object to |new_map| and set proper value for property 1442 // |double_descriptor|. Call GC and ensure that it did not crash during 1443 // slots buffer entries updating. 1444 1445 Handle<JSObject> obj; 1446 Handle<HeapObject> obj_value; 1447 Page* ec_page; 1448 { 1449 AlwaysAllocateScope always_allocate(isolate); 1450 obj = factory->NewJSObjectFromMap(map, TENURED); 1451 CHECK(old_space->Contains(*obj)); 1452 1453 // Make sure |obj_value| is placed on an old-space evacuation candidate. 1454 heap::SimulateFullSpace(old_space); 1455 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); 1456 ec_page = Page::FromAddress(obj_value->address()); 1457 CHECK_NE(ec_page, Page::FromAddress(obj->address())); 1458 } 1459 1460 // Heap is ready, force |ec_page| to become an evacuation candidate and 1461 // simulate incremental marking. 1462 ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); 1463 heap::SimulateIncrementalMarking(heap); 1464 1465 // Check that everything is ready for triggering incremental write barrier 1466 // (i.e. that both |obj| and |obj_value| are black and the marking phase is 1467 // still active and |obj_value|'s page is indeed an evacuation candidate). 1468 IncrementalMarking* marking = heap->incremental_marking(); 1469 CHECK(marking->IsMarking()); 1470 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); 1471 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj_value))); 1472 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); 1473 1474 // Trigger incremental write barrier, which should add a slot to remembered 1475 // set. 1476 { 1477 FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); 1478 obj->FastPropertyAtPut(index, *obj_value); 1479 } 1480 1481 // Migrate |obj| to |new_map| which should shift fields and put the 1482 // |boom_value| to the slot that was earlier recorded by incremental write 1483 // barrier. 1484 JSObject::MigrateToMap(obj, new_map); 1485 1486 double boom_value = bit_cast<double>(UINT64_C(0xbaad0176a37c28e1)); 1487 1488 FieldIndex double_field_index = 1489 FieldIndex::ForDescriptor(*new_map, double_descriptor); 1490 CHECK(obj->IsUnboxedDoubleField(double_field_index)); 1491 obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); 1492 1493 // Trigger GC to evacuate all candidates. 1494 CcTest::heap()->CollectGarbage(OLD_SPACE, "boom"); 1495 1496 // Ensure that the values are still there and correct. 1497 CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); 1498 1499 if (check_tagged_value) { 1500 FieldIndex tagged_field_index = 1501 FieldIndex::ForDescriptor(*new_map, tagged_descriptor); 1502 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); 1503 } 1504 CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); 1505 } 1506 1507 enum OldToWriteBarrierKind { 1508 OLD_TO_OLD_WRITE_BARRIER, 1509 OLD_TO_NEW_WRITE_BARRIER 1510 }; 1511 static void TestWriteBarrierObjectShiftFieldsRight( 1512 OldToWriteBarrierKind write_barrier_kind) { 1513 CcTest::InitializeVM(); 1514 Isolate* isolate = CcTest::i_isolate(); 1515 v8::HandleScope scope(CcTest::isolate()); 1516 1517 Handle<FieldType> any_type = FieldType::Any(isolate); 1518 1519 CompileRun("function func() { return 1; }"); 1520 1521 Handle<JSObject> func = GetObject("func"); 1522 1523 Handle<Map> map = Map::Create(isolate, 10); 1524 map = Map::CopyWithConstant(map, MakeName("prop", 0), func, NONE, 1525 INSERT_TRANSITION).ToHandleChecked(); 1526 map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, 1527 Representation::Double(), 1528 INSERT_TRANSITION).ToHandleChecked(); 1529 map = Map::CopyWithField(map, MakeName("prop", 2), any_type, NONE, 1530 Representation::Tagged(), 1531 INSERT_TRANSITION).ToHandleChecked(); 1532 1533 // Shift fields right by turning constant property to a field. 1534 Handle<Map> new_map = Map::ReconfigureProperty( 1535 map, 0, kData, NONE, Representation::Tagged(), any_type, FORCE_FIELD); 1536 1537 if (write_barrier_kind == OLD_TO_NEW_WRITE_BARRIER) { 1538 TestWriteBarrier(map, new_map, 2, 1); 1539 } else { 1540 CHECK_EQ(OLD_TO_OLD_WRITE_BARRIER, write_barrier_kind); 1541 TestIncrementalWriteBarrier(map, new_map, 2, 1); 1542 } 1543 } 1544 1545 TEST(WriteBarrierObjectShiftFieldsRight) { 1546 TestWriteBarrierObjectShiftFieldsRight(OLD_TO_NEW_WRITE_BARRIER); 1547 } 1548 1549 1550 TEST(IncrementalWriteBarrierObjectShiftFieldsRight) { 1551 TestWriteBarrierObjectShiftFieldsRight(OLD_TO_OLD_WRITE_BARRIER); 1552 } 1553 1554 1555 // TODO(ishell): add respective tests for property kind reconfiguring from 1556 // accessor field to double, once accessor fields are supported by 1557 // Map::ReconfigureProperty(). 1558 1559 1560 // TODO(ishell): add respective tests for fast property removal case once 1561 // Map::ReconfigureProperty() supports that. 1562 1563 #endif 1564