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