OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <stdlib.h> | 5 #include <stdlib.h> |
6 #include <utility> | 6 #include <utility> |
7 | 7 |
8 #include "src/v8.h" | 8 #include "src/v8.h" |
9 | 9 |
10 #include "src/compilation-cache.h" | 10 #include "src/compilation-cache.h" |
(...skipping 30 matching lines...) Expand all Loading... |
41 } | 41 } |
42 | 42 |
43 | 43 |
44 static Handle<String> MakeName(const char* str, int suffix) { | 44 static Handle<String> MakeName(const char* str, int suffix) { |
45 EmbeddedVector<char, 128> buffer; | 45 EmbeddedVector<char, 128> buffer; |
46 SNPrintF(buffer, "%s%d", str, suffix); | 46 SNPrintF(buffer, "%s%d", str, suffix); |
47 return MakeString(buffer.start()); | 47 return MakeString(buffer.start()); |
48 } | 48 } |
49 | 49 |
50 | 50 |
| 51 Handle<JSObject> GetObject(const char* name) { |
| 52 return v8::Utils::OpenHandle( |
| 53 *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str(name)))); |
| 54 } |
| 55 |
| 56 |
51 static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { | 57 static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { |
52 if (obj->IsUnboxedDoubleField(field_index)) { | 58 if (obj->IsUnboxedDoubleField(field_index)) { |
53 return obj->RawFastDoublePropertyAt(field_index); | 59 return obj->RawFastDoublePropertyAt(field_index); |
54 } else { | 60 } else { |
55 Object* value = obj->RawFastPropertyAt(field_index); | 61 Object* value = obj->RawFastPropertyAt(field_index); |
56 DCHECK(value->IsMutableHeapNumber()); | 62 DCHECK(value->IsMutableHeapNumber()); |
57 return HeapNumber::cast(value)->value(); | 63 return HeapNumber::cast(value)->value(); |
58 } | 64 } |
59 } | 65 } |
60 | 66 |
(...skipping 1320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1381 Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); | 1387 Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); |
1382 Handle<JSObject> clone(JSObject::cast(clone_obj)); | 1388 Handle<JSObject> clone(JSObject::cast(clone_obj)); |
1383 CHECK(heap->old_pointer_space()->Contains(clone->address())); | 1389 CHECK(heap->old_pointer_space()->Contains(clone->address())); |
1384 | 1390 |
1385 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | 1391 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); |
1386 | 1392 |
1387 // The value in cloned object should not be corrupted by GC. | 1393 // The value in cloned object should not be corrupted by GC. |
1388 CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); | 1394 CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); |
1389 } | 1395 } |
1390 | 1396 |
| 1397 |
| 1398 static void TestWriteBarrier(Handle<Map> map, Handle<Map> new_map, |
| 1399 int tagged_descriptor, int double_descriptor, |
| 1400 bool check_tagged_value = true) { |
| 1401 FLAG_stress_compaction = true; |
| 1402 FLAG_manual_evacuation_candidates_selection = true; |
| 1403 Isolate* isolate = CcTest::i_isolate(); |
| 1404 Factory* factory = isolate->factory(); |
| 1405 Heap* heap = CcTest::heap(); |
| 1406 PagedSpace* old_pointer_space = heap->old_pointer_space(); |
| 1407 |
| 1408 // The plan: create |obj| by |map| in old space, create |obj_value| in |
| 1409 // new space and ensure that write barrier is triggered when |obj_value| is |
| 1410 // written to property |tagged_descriptor| of |obj|. |
| 1411 // Then migrate object to |new_map| and set proper value for property |
| 1412 // |double_descriptor|. Call GC and ensure that it did not crash during |
| 1413 // store buffer entries updating. |
| 1414 |
| 1415 Handle<JSObject> obj; |
| 1416 Handle<HeapObject> obj_value; |
| 1417 { |
| 1418 AlwaysAllocateScope always_allocate(isolate); |
| 1419 obj = factory->NewJSObjectFromMap(map, TENURED, false); |
| 1420 CHECK(old_pointer_space->Contains(*obj)); |
| 1421 |
| 1422 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS); |
| 1423 } |
| 1424 |
| 1425 CHECK(heap->InNewSpace(*obj_value)); |
| 1426 |
| 1427 { |
| 1428 FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); |
| 1429 const int n = 153; |
| 1430 for (int i = 0; i < n; i++) { |
| 1431 obj->FastPropertyAtPut(index, *obj_value); |
| 1432 } |
| 1433 } |
| 1434 |
| 1435 // Migrate |obj| to |new_map| which should shift fields and put the |
| 1436 // |boom_value| to the slot that was earlier recorded by write barrier. |
| 1437 JSObject::MigrateToMap(obj, new_map); |
| 1438 |
| 1439 Address fake_object = reinterpret_cast<Address>(*obj_value) + kPointerSize; |
| 1440 double boom_value = bit_cast<double>(fake_object); |
| 1441 |
| 1442 FieldIndex double_field_index = |
| 1443 FieldIndex::ForDescriptor(*new_map, double_descriptor); |
| 1444 CHECK(obj->IsUnboxedDoubleField(double_field_index)); |
| 1445 obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); |
| 1446 |
| 1447 // Trigger GC to evacuate all candidates. |
| 1448 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); |
| 1449 |
| 1450 if (check_tagged_value) { |
| 1451 FieldIndex tagged_field_index = |
| 1452 FieldIndex::ForDescriptor(*new_map, tagged_descriptor); |
| 1453 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); |
| 1454 } |
| 1455 CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); |
| 1456 } |
| 1457 |
| 1458 |
| 1459 static void TestIncrementalWriteBarrier(Handle<Map> map, Handle<Map> new_map, |
| 1460 int tagged_descriptor, |
| 1461 int double_descriptor, |
| 1462 bool check_tagged_value = true) { |
| 1463 if (FLAG_never_compact || !FLAG_incremental_marking) return; |
| 1464 FLAG_stress_compaction = true; |
| 1465 FLAG_manual_evacuation_candidates_selection = true; |
| 1466 Isolate* isolate = CcTest::i_isolate(); |
| 1467 Factory* factory = isolate->factory(); |
| 1468 Heap* heap = CcTest::heap(); |
| 1469 PagedSpace* old_pointer_space = heap->old_pointer_space(); |
| 1470 |
| 1471 // The plan: create |obj| by |map| in old space, create |obj_value| in |
| 1472 // old space and ensure it end up in evacuation candidate page. Start |
| 1473 // incremental marking and ensure that incremental write barrier is triggered |
| 1474 // when |obj_value| is written to property |tagged_descriptor| of |obj|. |
| 1475 // Then migrate object to |new_map| and set proper value for property |
| 1476 // |double_descriptor|. Call GC and ensure that it did not crash during |
| 1477 // slots buffer entries updating. |
| 1478 |
| 1479 Handle<JSObject> obj; |
| 1480 Handle<HeapObject> obj_value; |
| 1481 Page* ec_page; |
| 1482 { |
| 1483 AlwaysAllocateScope always_allocate(isolate); |
| 1484 obj = factory->NewJSObjectFromMap(map, TENURED, false); |
| 1485 CHECK(old_pointer_space->Contains(*obj)); |
| 1486 |
| 1487 // Make sure |obj_value| is placed on an old-space evacuation candidate. |
| 1488 SimulateFullSpace(old_pointer_space); |
| 1489 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); |
| 1490 ec_page = Page::FromAddress(obj_value->address()); |
| 1491 CHECK_NE(ec_page, Page::FromAddress(obj->address())); |
| 1492 } |
| 1493 |
| 1494 // Heap is ready, force |ec_page| to become an evacuation candidate and |
| 1495 // simulate incremental marking. |
| 1496 ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); |
| 1497 SimulateIncrementalMarking(heap); |
| 1498 |
| 1499 // Check that everything is ready for triggering incremental write barrier |
| 1500 // (i.e. that both |obj| and |obj_value| are black and the marking phase is |
| 1501 // still active and |obj_value|'s page is indeed an evacuation candidate). |
| 1502 IncrementalMarking* marking = heap->incremental_marking(); |
| 1503 CHECK(marking->IsMarking()); |
| 1504 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); |
| 1505 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj_value))); |
| 1506 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
| 1507 |
| 1508 // Trigger incremental write barrier, which should add a slot to |ec_page|'s |
| 1509 // slots buffer. |
| 1510 { |
| 1511 int slots_buffer_len = SlotsBuffer::SizeOfChain(ec_page->slots_buffer()); |
| 1512 FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); |
| 1513 const int n = SlotsBuffer::kNumberOfElements + 10; |
| 1514 for (int i = 0; i < n; i++) { |
| 1515 obj->FastPropertyAtPut(index, *obj_value); |
| 1516 } |
| 1517 // Ensure that the slot was actually added to the |ec_page|'s slots buffer. |
| 1518 CHECK_EQ(slots_buffer_len + n, |
| 1519 SlotsBuffer::SizeOfChain(ec_page->slots_buffer())); |
| 1520 } |
| 1521 |
| 1522 // Migrate |obj| to |new_map| which should shift fields and put the |
| 1523 // |boom_value| to the slot that was earlier recorded by incremental write |
| 1524 // barrier. |
| 1525 JSObject::MigrateToMap(obj, new_map); |
| 1526 |
| 1527 double boom_value = bit_cast<double>(UINT64_C(0xbaad0176a37c28e1)); |
| 1528 |
| 1529 FieldIndex double_field_index = |
| 1530 FieldIndex::ForDescriptor(*new_map, double_descriptor); |
| 1531 CHECK(obj->IsUnboxedDoubleField(double_field_index)); |
| 1532 obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); |
| 1533 |
| 1534 // Trigger GC to evacuate all candidates. |
| 1535 CcTest::heap()->CollectGarbage(OLD_POINTER_SPACE, "boom"); |
| 1536 |
| 1537 // Ensure that the values are still there and correct. |
| 1538 CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
| 1539 |
| 1540 if (check_tagged_value) { |
| 1541 FieldIndex tagged_field_index = |
| 1542 FieldIndex::ForDescriptor(*new_map, tagged_descriptor); |
| 1543 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); |
| 1544 } |
| 1545 CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); |
| 1546 } |
| 1547 |
| 1548 |
| 1549 enum WriteBarrierKind { OLD_TO_OLD_WRITE_BARRIER, OLD_TO_NEW_WRITE_BARRIER }; |
| 1550 static void TestWriteBarrierObjectShiftFieldsRight( |
| 1551 WriteBarrierKind write_barrier_kind) { |
| 1552 CcTest::InitializeVM(); |
| 1553 Isolate* isolate = CcTest::i_isolate(); |
| 1554 v8::HandleScope scope(CcTest::isolate()); |
| 1555 |
| 1556 Handle<HeapType> any_type = HeapType::Any(isolate); |
| 1557 |
| 1558 CompileRun("function func() { return 1; }"); |
| 1559 |
| 1560 Handle<JSObject> func = GetObject("func"); |
| 1561 |
| 1562 Handle<Map> map = Map::Create(isolate, 10); |
| 1563 map = Map::CopyWithConstant(map, MakeName("prop", 0), func, NONE, |
| 1564 INSERT_TRANSITION).ToHandleChecked(); |
| 1565 map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, |
| 1566 Representation::Double(), |
| 1567 INSERT_TRANSITION).ToHandleChecked(); |
| 1568 map = Map::CopyWithField(map, MakeName("prop", 2), any_type, NONE, |
| 1569 Representation::Tagged(), |
| 1570 INSERT_TRANSITION).ToHandleChecked(); |
| 1571 |
| 1572 // Shift fields right by turning constant property to a field. |
| 1573 Handle<Map> new_map = Map::ReconfigureProperty( |
| 1574 map, 0, kData, NONE, Representation::Tagged(), any_type, FORCE_FIELD); |
| 1575 |
| 1576 if (write_barrier_kind == OLD_TO_NEW_WRITE_BARRIER) { |
| 1577 TestWriteBarrier(map, new_map, 2, 1); |
| 1578 } else { |
| 1579 CHECK_EQ(OLD_TO_OLD_WRITE_BARRIER, write_barrier_kind); |
| 1580 TestIncrementalWriteBarrier(map, new_map, 2, 1); |
| 1581 } |
| 1582 } |
| 1583 |
| 1584 |
| 1585 // TODO(ishell): enable when this issue is fixed. |
| 1586 DISABLED_TEST(WriteBarrierObjectShiftFieldsRight) { |
| 1587 TestWriteBarrierObjectShiftFieldsRight(OLD_TO_NEW_WRITE_BARRIER); |
| 1588 } |
| 1589 |
| 1590 |
| 1591 TEST(IncrementalWriteBarrierObjectShiftFieldsRight) { |
| 1592 TestWriteBarrierObjectShiftFieldsRight(OLD_TO_OLD_WRITE_BARRIER); |
| 1593 } |
| 1594 |
| 1595 |
| 1596 // TODO(ishell): add respective tests for property kind reconfiguring from |
| 1597 // accessor field to double, once accessor fields are supported by |
| 1598 // Map::ReconfigureProperty(). |
| 1599 |
| 1600 |
| 1601 // TODO(ishell): add respective tests for fast property removal case once |
| 1602 // Map::ReconfigureProperty() supports that. |
| 1603 |
1391 #endif | 1604 #endif |
OLD | NEW |