| 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 1237 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1298   Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); | 1304   Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); | 
| 1299   Handle<JSObject> clone(JSObject::cast(clone_obj)); | 1305   Handle<JSObject> clone(JSObject::cast(clone_obj)); | 
| 1300   CHECK(heap->old_pointer_space()->Contains(clone->address())); | 1306   CHECK(heap->old_pointer_space()->Contains(clone->address())); | 
| 1301 | 1307 | 
| 1302   CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | 1308   CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | 
| 1303 | 1309 | 
| 1304   // The value in cloned object should not be corrupted by GC. | 1310   // The value in cloned object should not be corrupted by GC. | 
| 1305   CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); | 1311   CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); | 
| 1306 } | 1312 } | 
| 1307 | 1313 | 
|  | 1314 | 
|  | 1315 static void TestWriteBarrier(Handle<Map> map, Handle<Map> new_map, | 
|  | 1316                              int tagged_descriptor, int double_descriptor, | 
|  | 1317                              bool check_tagged_value = true) { | 
|  | 1318   FLAG_stress_compaction = true; | 
|  | 1319   FLAG_manual_evacuation_candidates_selection = true; | 
|  | 1320   Isolate* isolate = CcTest::i_isolate(); | 
|  | 1321   Factory* factory = isolate->factory(); | 
|  | 1322   Heap* heap = CcTest::heap(); | 
|  | 1323   PagedSpace* old_pointer_space = heap->old_pointer_space(); | 
|  | 1324 | 
|  | 1325   // The plan: create |obj| by |map| in old space, create |obj_value| in | 
|  | 1326   // new space and ensure that write barrier is triggered when |obj_value| is | 
|  | 1327   // written to property |tagged_descriptor| of |obj|. | 
|  | 1328   // Then migrate object to |new_map| and set proper value for property | 
|  | 1329   // |double_descriptor|. Call GC and ensure that it did not crash during | 
|  | 1330   // store buffer entries updating. | 
|  | 1331 | 
|  | 1332   Handle<JSObject> obj; | 
|  | 1333   Handle<HeapObject> obj_value; | 
|  | 1334   { | 
|  | 1335     AlwaysAllocateScope always_allocate(isolate); | 
|  | 1336     obj = factory->NewJSObjectFromMap(map, TENURED, false); | 
|  | 1337     CHECK(old_pointer_space->Contains(*obj)); | 
|  | 1338 | 
|  | 1339     obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS); | 
|  | 1340   } | 
|  | 1341 | 
|  | 1342   CHECK(heap->InNewSpace(*obj_value)); | 
|  | 1343 | 
|  | 1344   StoreBuffer* store_buffer = heap->store_buffer(); | 
|  | 1345   Address slot; | 
|  | 1346   { | 
|  | 1347     FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); | 
|  | 1348     int offset = index.offset(); | 
|  | 1349     slot = reinterpret_cast<Address>(HeapObject::RawField(*obj, offset)); | 
|  | 1350     USE(slot); | 
|  | 1351     DCHECK(!store_buffer->CellIsInStoreBuffer(slot)); | 
|  | 1352 | 
|  | 1353     const int n = 153; | 
|  | 1354     for (int i = 0; i < n; i++) { | 
|  | 1355       obj->FastPropertyAtPut(index, *obj_value); | 
|  | 1356     } | 
|  | 1357     // Ensure that the slot was actually added to the store buffer. | 
|  | 1358     DCHECK(store_buffer->CellIsInStoreBuffer(slot)); | 
|  | 1359   } | 
|  | 1360 | 
|  | 1361   // Migrate |obj| to |new_map| which should shift fields and put the | 
|  | 1362   // |boom_value| to the slot that was earlier recorded by write barrier. | 
|  | 1363   JSObject::MigrateToMap(obj, new_map); | 
|  | 1364 | 
|  | 1365   // Ensure that invalid entries were removed from the store buffer. | 
|  | 1366   DCHECK(!store_buffer->CellIsInStoreBuffer(slot)); | 
|  | 1367 | 
|  | 1368   Address fake_object = reinterpret_cast<Address>(*obj_value) + kPointerSize; | 
|  | 1369   double boom_value = bit_cast<double>(fake_object); | 
|  | 1370 | 
|  | 1371   FieldIndex double_field_index = | 
|  | 1372       FieldIndex::ForDescriptor(*new_map, double_descriptor); | 
|  | 1373   CHECK(obj->IsUnboxedDoubleField(double_field_index)); | 
|  | 1374   obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); | 
|  | 1375 | 
|  | 1376   // Trigger GC to evacuate all candidates. | 
|  | 1377   CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | 
|  | 1378 | 
|  | 1379   if (check_tagged_value) { | 
|  | 1380     FieldIndex tagged_field_index = | 
|  | 1381         FieldIndex::ForDescriptor(*new_map, tagged_descriptor); | 
|  | 1382     CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); | 
|  | 1383   } | 
|  | 1384   CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); | 
|  | 1385 } | 
|  | 1386 | 
|  | 1387 | 
|  | 1388 static void TestIncrementalWriteBarrier(Handle<Map> map, Handle<Map> new_map, | 
|  | 1389                                         int tagged_descriptor, | 
|  | 1390                                         int double_descriptor, | 
|  | 1391                                         bool check_tagged_value = true) { | 
|  | 1392   if (FLAG_never_compact || !FLAG_incremental_marking) return; | 
|  | 1393   FLAG_stress_compaction = true; | 
|  | 1394   FLAG_manual_evacuation_candidates_selection = true; | 
|  | 1395   Isolate* isolate = CcTest::i_isolate(); | 
|  | 1396   Factory* factory = isolate->factory(); | 
|  | 1397   Heap* heap = CcTest::heap(); | 
|  | 1398   PagedSpace* old_pointer_space = heap->old_pointer_space(); | 
|  | 1399 | 
|  | 1400   // The plan: create |obj| by |map| in old space, create |obj_value| in | 
|  | 1401   // old space and ensure it end up in evacuation candidate page. Start | 
|  | 1402   // incremental marking and ensure that incremental write barrier is triggered | 
|  | 1403   // when |obj_value| is written to property |tagged_descriptor| of |obj|. | 
|  | 1404   // Then migrate object to |new_map| and set proper value for property | 
|  | 1405   // |double_descriptor|. Call GC and ensure that it did not crash during | 
|  | 1406   // slots buffer entries updating. | 
|  | 1407 | 
|  | 1408   Handle<JSObject> obj; | 
|  | 1409   Handle<HeapObject> obj_value; | 
|  | 1410   Page* ec_page; | 
|  | 1411   { | 
|  | 1412     AlwaysAllocateScope always_allocate(isolate); | 
|  | 1413     obj = factory->NewJSObjectFromMap(map, TENURED, false); | 
|  | 1414     CHECK(old_pointer_space->Contains(*obj)); | 
|  | 1415 | 
|  | 1416     // Make sure |obj_value| is placed on an old-space evacuation candidate. | 
|  | 1417     SimulateFullSpace(old_pointer_space); | 
|  | 1418     obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); | 
|  | 1419     ec_page = Page::FromAddress(obj_value->address()); | 
|  | 1420     CHECK_NE(ec_page, Page::FromAddress(obj->address())); | 
|  | 1421   } | 
|  | 1422 | 
|  | 1423   // Heap is ready, force |ec_page| to become an evacuation candidate and | 
|  | 1424   // simulate incremental marking. | 
|  | 1425   ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); | 
|  | 1426   SimulateIncrementalMarking(heap); | 
|  | 1427 | 
|  | 1428   // Check that everything is ready for triggering incremental write barrier | 
|  | 1429   // (i.e. that both |obj| and |obj_value| are black and the marking phase is | 
|  | 1430   // still active and |obj_value|'s page is indeed an evacuation candidate). | 
|  | 1431   IncrementalMarking* marking = heap->incremental_marking(); | 
|  | 1432   CHECK(marking->IsMarking()); | 
|  | 1433   CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); | 
|  | 1434   CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj_value))); | 
|  | 1435   CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); | 
|  | 1436 | 
|  | 1437   // Trigger incremental write barrier, which should add a slot to |ec_page|'s | 
|  | 1438   // slots buffer. | 
|  | 1439   { | 
|  | 1440     int slots_buffer_len = SlotsBuffer::SizeOfChain(ec_page->slots_buffer()); | 
|  | 1441     FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); | 
|  | 1442     const int n = SlotsBuffer::kNumberOfElements + 10; | 
|  | 1443     for (int i = 0; i < n; i++) { | 
|  | 1444       obj->FastPropertyAtPut(index, *obj_value); | 
|  | 1445     } | 
|  | 1446     // Ensure that the slot was actually added to the |ec_page|'s slots buffer. | 
|  | 1447     CHECK_EQ(slots_buffer_len + n, | 
|  | 1448              SlotsBuffer::SizeOfChain(ec_page->slots_buffer())); | 
|  | 1449   } | 
|  | 1450 | 
|  | 1451   // Migrate |obj| to |new_map| which should shift fields and put the | 
|  | 1452   // |boom_value| to the slot that was earlier recorded by incremental write | 
|  | 1453   // barrier. | 
|  | 1454   JSObject::MigrateToMap(obj, new_map); | 
|  | 1455 | 
|  | 1456   double boom_value = bit_cast<double>(UINT64_C(0xbaad0176a37c28e1)); | 
|  | 1457 | 
|  | 1458   FieldIndex double_field_index = | 
|  | 1459       FieldIndex::ForDescriptor(*new_map, double_descriptor); | 
|  | 1460   CHECK(obj->IsUnboxedDoubleField(double_field_index)); | 
|  | 1461   obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); | 
|  | 1462 | 
|  | 1463   // Trigger GC to evacuate all candidates. | 
|  | 1464   CcTest::heap()->CollectGarbage(OLD_POINTER_SPACE, "boom"); | 
|  | 1465 | 
|  | 1466   // Ensure that the values are still there and correct. | 
|  | 1467   CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); | 
|  | 1468 | 
|  | 1469   if (check_tagged_value) { | 
|  | 1470     FieldIndex tagged_field_index = | 
|  | 1471         FieldIndex::ForDescriptor(*new_map, tagged_descriptor); | 
|  | 1472     CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); | 
|  | 1473   } | 
|  | 1474   CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); | 
|  | 1475 } | 
|  | 1476 | 
|  | 1477 | 
|  | 1478 enum WriteBarrierKind { OLD_TO_OLD_WRITE_BARRIER, OLD_TO_NEW_WRITE_BARRIER }; | 
|  | 1479 static void TestWriteBarrierObjectShiftFieldsRight( | 
|  | 1480     WriteBarrierKind write_barrier_kind) { | 
|  | 1481   CcTest::InitializeVM(); | 
|  | 1482   Isolate* isolate = CcTest::i_isolate(); | 
|  | 1483   v8::HandleScope scope(CcTest::isolate()); | 
|  | 1484 | 
|  | 1485   Handle<HeapType> any_type = HeapType::Any(isolate); | 
|  | 1486 | 
|  | 1487   CompileRun("function func() { return 1; }"); | 
|  | 1488 | 
|  | 1489   Handle<JSObject> func = GetObject("func"); | 
|  | 1490 | 
|  | 1491   Handle<Map> map = Map::Create(isolate, 10); | 
|  | 1492   map = Map::CopyWithConstant(map, MakeName("prop", 0), func, NONE, | 
|  | 1493                               INSERT_TRANSITION).ToHandleChecked(); | 
|  | 1494   map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, | 
|  | 1495                            Representation::Double(), | 
|  | 1496                            INSERT_TRANSITION).ToHandleChecked(); | 
|  | 1497   map = Map::CopyWithField(map, MakeName("prop", 2), any_type, NONE, | 
|  | 1498                            Representation::Tagged(), | 
|  | 1499                            INSERT_TRANSITION).ToHandleChecked(); | 
|  | 1500 | 
|  | 1501   // Shift fields right by turning constant property to a field. | 
|  | 1502   Handle<Map> new_map = Map::ReconfigureProperty( | 
|  | 1503       map, 0, kData, NONE, Representation::Tagged(), any_type, FORCE_FIELD); | 
|  | 1504 | 
|  | 1505   if (write_barrier_kind == OLD_TO_NEW_WRITE_BARRIER) { | 
|  | 1506     TestWriteBarrier(map, new_map, 2, 1); | 
|  | 1507   } else { | 
|  | 1508     CHECK_EQ(OLD_TO_OLD_WRITE_BARRIER, write_barrier_kind); | 
|  | 1509     TestIncrementalWriteBarrier(map, new_map, 2, 1); | 
|  | 1510   } | 
|  | 1511 } | 
|  | 1512 | 
|  | 1513 | 
|  | 1514 TEST(WriteBarrierObjectShiftFieldsRight) { | 
|  | 1515   TestWriteBarrierObjectShiftFieldsRight(OLD_TO_NEW_WRITE_BARRIER); | 
|  | 1516 } | 
|  | 1517 | 
|  | 1518 | 
|  | 1519 TEST(IncrementalWriteBarrierObjectShiftFieldsRight) { | 
|  | 1520   TestWriteBarrierObjectShiftFieldsRight(OLD_TO_OLD_WRITE_BARRIER); | 
|  | 1521 } | 
|  | 1522 | 
|  | 1523 | 
|  | 1524 // TODO(ishell): add respective tests for property kind reconfiguring from | 
|  | 1525 // accessor field to double, once accessor fields are supported by | 
|  | 1526 // Map::ReconfigureProperty(). | 
|  | 1527 | 
|  | 1528 | 
|  | 1529 // TODO(ishell): add respective tests for fast property removal case once | 
|  | 1530 // Map::ReconfigureProperty() supports that. | 
|  | 1531 | 
| 1308 #endif | 1532 #endif | 
| OLD | NEW | 
|---|